Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

SharePoint 2013: Angular 1.x & Bootstrap Slider

5.00/5 (2 votes)
10 Jun 2016CPOL3 min read 14.1K   5  
Angular SPA Bootstrap Image Slider for SharePoint 2013

Introduction

This article is meant to provide guidance and insight into working with AngularJS 1.x and Bootstrap in a restricted SharePoint hosting environment. Do you work in a SharePoint environment where add-ins and solutions are not allowed? A common UI element that people like is an images slider. This article gives an example of what to do in a restricted development environment when you really "need" something cool on your SharePoint page. The following code is meant as a starting point. Significant changes can be made to the slider via manipulation of the JavaScript code and CSS rules. Any column names or other labeling not associated with the Angular directives or Bootstrap library/CSS can be changed to better fit your use scenario. "<" ">" symbols denote content (e.g. URL and GUID) particular to your environment.

Background

With SharePoint 2016 around the corner, migrations to Office 365 a serious consideration for some and the desire to not "re-code" applications companies are advocating against Solution and Feature development. Furthermore, the creation and investment in add-in development has a "just another Silverlight" effect on some. This trepidation can initially be experienced by developers as a total bummer but there is a solution. My go to solution is to create Angular Single Page Application (SPA)s. Angular2 is revolutionary but 1.x can still address most of your needs. With AngularJS Bootstrap and the 2013 REST API, there are few limitations to what you can achieve. My mantra here is to use SharePoint base functions (Security permissions, Lists, etc.) and render UI elements with JavaScript.

Requirements

  1. angular.js (recommend min)
  2. bootstrap.js (recommend min)
  3. bootstrap.css (recommend min)
  4. SharePoint Picture List with the following additional columns:
    • Comment: Single line of text
    • LinkURL: Hyperlink or Picture
    • ListOrder: Choice (add numbers starting with 0)
    • Initiative: Lookup (this column will provide the querystring association – as needed)

Using the Code

A great way to save time is to minimize modifications to the base Bootstrap css. However, depending on your needs, you may find this an impossible thing to do. Stay away from modifying the Bootstrap file and add the needed changes to your SPA.

CSS
<style type="text/css">
.carousel { }
.carousel-indicators li {background-color: #000\9; border: 1px solid #000000 !important;}
.carousel-indicators .active {background-color: #000000 !important}
.carousel-caption {color:#000000 !important; text-shadow:none !important; padding-top:0px !important}
.outlineMyCarousel {border: 1px solid #808080!important; padding:1px !important}
</style>

Normally, I would separate JavaScript functions and variables to a separate "utility" file but for simplicity, in this article, I have combined my JavaScript with the AngularJS used.

Please see:

JavaScript
<script type="text/javascript">
//openNewSliderImageDialog calls the dialog window for the image file upload target URL
function openNewSliderImageDialog(tUrl, tTitle) {
var options = {
url: tUrl,
title: tTitle
}; SP.UI.ModalDialog.showModalDialog(options);
}
//getParameterByName is used to extract querystring values later used 
//to associate with a SharePoint list column field value
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
//cd1 is the location of the images used for the slider (images should be the same size)
var cd1 = 'SliderImages/Forms/Thumbnails.aspx';
//initiativeName calls the querystring value that will be used to display the appropriate images
var initiativeName = getParameterByName('Init');
//listTitle is the name of the SharePiont list where images are uploaded
var listTitle = 'SliderImages';
//dTarget is the dialog id
var dTarget = '#myCarousel';
//spDomain is the root URL
var spDomain = "https://oursites.myngc.com/ENT/Innovation/";
//imgSource is the slider image URL before the image name
var imgSource = spDomain + 'SliderImages/';
// ************* Angular code starts here *******************
//sliderApp must be referenced in the html script
var app = angular.module('sliderApp', []);
//sliderCtrl must be referenced in the html script
app.controller('sliderCtrl', function ($scope, $http, sliderSvcs) {
//$scope objects are the {{referenced}} in the html script
$scope.CurrentReviewDate = new Date();
$scope.dTarget = dTarget;
//getEntries returns the data array to object sliderItems
sliderSvcs.getEntries(listTitle).then(function (result) {
$scope.sliderItems = result;
});
$scope.srcURL = spDomain;
$scope.imgSource = imgSource;
$scope.cd1 = $scope.srcURL + cd1;
});
//sliderSvcs must be added as a dependancy to sliderCtrl
app.service('sliderSvcs', ['$http', '$q', function ($http, $q) {
//header values required for SharePoint integration
$http.defaults.headers.common.Accept = "application/json;odata=verbose";
$http.defaults.headers.post['Content-Type'] = 'application/json;odata=verbose';
this.getEntries = function (listTitle) {
var dfd = $q.defer();
$http.defaults.headers.post['X-HTTP-Method'] = ""
//query statement calls list fields to be used, lookup list (xxx/xxx) values via expand, 
//orders by list field value "ListOrder" and filters via the list 
//field value identified in the query string [review SharePoint REST API]
var query = "?$select=Author/Title,Initiative/Title,FieldValuesAsText/FileRef,
ListOrder,Title,Comment&$expand=Author,Initiative,
FieldValuesAsText&$orderby=ListOrder asc&$filter=Initiative/Title 
eq '" + initiativeName + "'";
var restUrl = spDomain + "_api/web/lists/getByTitle
('" + listTitle + "')/items" + query;
$http.get(restUrl).success(function (data) {
dfd.resolve(data.d.results);
}).error(function (data) {
dfd.reject("error getting items");
});
return dfd.promise;
}
}]);
</script>

Finally, the html component.

HTML
<!--Angular and bootstrap references are integrated into html-->
<div ng-cloak ng-app="sliderApp" ng-controller="sliderCtrl">
<div>
<a href="#" onclick="openNewSliderImageDialog
('<mydomain>/_layouts/15/Upload.aspx?List={<sliderPhotoListGuid>}
&RootFolder=&IsDlg=1','New Slider Image');
" style="height: 30px; width: 30px;" 
data-toggle="tooltip" data-placement="left" 
title="Add a new Slider Image">
<i class="fa fa-plus-square-o fa-2x" 
aria-hidden="true"></i></a>
<a id="sliderList" ng-href="{{cd1}}" 
data-toggle="tooltip" 
data-placement="left" title="View Slider Image list">
<i class="fa fa-list fa-2x" aria-hidden="true" 
style="height: 30px; width: 30px;"></i></a>
</div>
<div id="myCarousel" 
class="carousel slide outlineMyCarousel" data-ride="carousel">
<ol class="carousel-indicators" style="color: #000000 !important">
<li ng-repeat="item in sliderItems" data-target="{{dTarget}}" 
data-slide-to="{{item.ListOrder}}" 
ng-class="{active:!$index}"></li>
</ol>
<div class="carousel-inner">
<div class="item" 
ng-class="{active:!$index}" ng-repeat="item in sliderItems">
<img ng-src="{{item.FieldValuesAsText.FileRef}}" alt="" 
class="img-responsive" style="min-height: 200px; min-width: 300px !important; 
max-height: 200px; min-width: 300px; text-shadow: none !important" />
<div class="carousel-caption">
<h3>{{item.Title}}</h3>
<p>{{item.Comment}}</p>
<p><i>-{{item.Author.Title}}</i></p>
</div>
</div>
</div>
<a class="left carousel-control" 
href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" 
aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" 
href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" 
aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>

Points of Interest

Accessing SharePoint list data with REST API brings more data to us than meets the eye. View in XML your data and then experiment with RoleAssignments, AttachmentFiles,

ContentType<br />
FieldValuesAsHtml
, FieldValuesAsText and FieldValuesForEdit. Similar to manipulating Lookup data, when you reference these sources, you usually find what you need. See how I use "FieldValuesAsText/FileRef" in the code above. Consider the following:

  1. Access the XML of your REST API (e.g. <mydomain>/_api/web/lists/getByTitle('Announcements')/items)
  2. See article: Getting document name using REST (not title) while filtering document library http://sharepoint.stackexchange.com/questions/126003/getting-document-name-using-rest-not-title-while-filtering-document-library
  3. See article: Get to know the SharePoint 2013 REST service
    https://msdn.microsoft.com/en-us/library/office/fp142380.aspx
  4. See article: Accelerate SharePoint Development with AngularJS
    http://www.sharepointbriefing.com/spcode/accelerate-sharepoint-development-with-angularjs.html

SharePoint Integration

  1. Place the SPA file (e.g. imageSlider.html) in a SharePoint document library, e.g. https://<mydomain>/Style%20Library/
  2. Place the required reference libraries in a SharePoint document library, e.g. https://<mydomain>/Style%20Library/js/
  3. On the page where the slider will appear:
    • Place the required references in a Script Editor. <script src="<mydomain>/Style%20Library/js/jquery-1.12.0.min.js"></script> <script src="<mydomain>/Style%20Library/js/bootstrap.min.js"></script> <link href="<mydomain>/Style%20Library/css/bootstrap_custom.min.css" rel="stylesheet" />
    • Insert a Content Editor Web Part (CEWP) where you want the slider to appear on the SharePoint page layout, insert the URL to the SLA file in the Content Link element and set Chrome Type to None (if desired).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)