Monday, June 19, 2017

SharePoint REST Services with AngularJS 2.0

Background

Our team had someone come to speak to us about AngularJS, however, they only gave an
overview of the framework, with no examples.  Since I had started reprogramming our team SharePoint site to utilize MS SharePoint REST services, I decided to see if I could leverage AngularJS also.

I decided to start easy and create a Proof of Concept before utilizing it on my app.  So, I decided to create a module what would search a list on my SharePoint site and create a link to our internal scorecard and create a link to another corresponding scorecard on another SharePoint site, given a particular project ID.   The very simple interface would look like this:

The value that will be returned will be the name of the project and two links, one to the EPP Scorecard and one to our internal scorecard.

The Point

The first thing you do once you create your .aspx page in SharePoint designer is to add the reference to AngularJS.  I store my plugins, frameworks and tools to one library that I call jsandjquerystuff under our SharePoint site for easier access:
<script type="text/javascript" src="http://xxx.sharepoint.com/.../ jsandjquerystuff/angular.min.js"></script>

Angular Part

AngularJS is a JavaScript implementation of the Model-View-Controller architecture which is a way of separating internal representation of data from the presentation and that is all I will say about that. Let’s get to the good stuff.  Now the reason I mentioned the nerdy stuff above is because it will help explain why some AngularJS functions the way it does. 
With the added code from the AngularJS framework come additional attributes for HTML tags, namely the following:
·        ng-app: Defines an application in a <div/>
o   <div ng-app="myApp" ng-controller="myCtrl">
·        ng-controller: Defines the controller (think of them like buttons, input fields, etc on a page)
o   <div ng-app="myApp" ng-controller="myCtrl">
·        ng-model: binds the value of a field to a value defined in an <input/> tag
o   <input type="text" ng-model="search">



The Angular View/Model

<!-- (1) Create the controller -->
<div ng-app="myApp" ng-controller="myCtrl">
<!-- (2) Serves as the model. Binds prjID with whatever is provided in the input variable -->
<p>Project ID:<input type="text" ng-model="search"></p>
<button ng-click="myFunc()">Search</button>
</div>

The first <div/> ties the app to the controller and further binds the value typed into the textbox to the variable ‘search’.
When the button is clicked it calls a JavaScript function called ‘myFunc()’.
The HTML produces this textbox and prompt.
<div>
Project Name: {{pname}} <a target="_blank" "id="int" ng-href="{{fullLocInt}}">Internal</a> <a target="_blank" id="epp" ng-href="{{fullLocEpp}}">EPP</a>
</div>

Values returned from the controller are placed into the ‘placeholders’ designated through the double braces:
{{expression}}

As can be seen by the example to the left, the expression can be displayed or used to hold data for other purposes. In this example, they are used to create a hyperlink.
The HTML produces this placeholder. When the program is run, the returned project name will follow the ‘Project Name:’ and ‘Internal’ and ‘EPP’ will both be hyperlinks to the respective scorecards.

The Angular Controller/SharePoint REST Services

Now for the meat of the matter, how do we get the scorecards?  Well, honestly that part is rather easy. Once I have the scorecard from our internal site, I can easily create the link to the EPP scorecard.  So, I’m really just searching one list.  That is accomplished this way:
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('myCtrl',
        function($scope, $http) //$scope binds HTML and JavaScript | $http: makes request to server and returns response.
        {
        $scope.myFunc=function()
        {
            $http({
                method: "GET",
                url:
" http://xxx.sharepoint.com/.../ _api/web/lists/getbytitle('PotentialProjectsLOE')/items?$filter=ProjectID eq '" + $scope.search + "'",
                headers: { "accept":"application/json;odata=verbose"}
                })//end http
                .then(
                function mysuccess(data, status, xhr)
                {
                        if (data.data.d.results.length==0)
                        {
                            alert("Please enter a VALID Project ID.");
                        }
                        else
                        {
                        $scope.pname=data.data.d.results[0].Title;
                        $scope.intid=data.data.d.results[0].ID;
                        $scope.eppid=data.data.d.results[0].EPPID;
                        $scope.fullLocInt="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/Lists/PotentialProjectsLOE/Item/displayifs.aspx?ID=" + $scope.intid;                       
                        $scope.fullLocEpp=" http://xxx.sharepoint.com/.../ Project/Lists/Project%20Status/item/displayifs.aspx?ID=" + $scope.eppid;
                        }
                },
                function epicfail(data, status, xhr)
                {
                    alert("Well...that sucked.  It didn't work.");
                });
        }
        });//end controller
</script>

As I stated before, the variable ‘search’ holds the value typed in by the user into the Project ID.  This is where we put that variable to work.
The SharePoint service I used for this effort was the ‘getbyTitle’ service.  You simply pass the name of the list and optionally any values you wish to use to filter the results:
" http://xxx.sharepoint.com/.../ _api/web/lists/getbytitle('PotentialProjectsLOE')/items?$filter=ProjectID eq '" + $scope.search + "'"

This tells the service to search the PotentialProjecsLOE list and return any item that has the project ID entered by the user in the ‘search’ variable. If it finds the item, it places the Title, ID and EPPID into variables in the $Scope object. The project name is placed in pname.

I also created the variables fullLocInt to represent the link to the internal scorecard and fullLocEPP to represent the link to the EPP scorecard.
<div>
Project Name: {{pname}} <a target="_blank" "id="int" ng-href="{{fullLocInt}}">Internal</a> <a target="_blank" id="epp" ng-href="{{fullLocEpp}}">EPP</a>
</div>
Pname, fullLocInt and fullLocEPP are then returned to the View.
$Scope is a double agent. $Scope binds the HTML and the JavaScript and is available to both the controller and the view.  Basically, it is the Oracle of the app, it knows all. It holds the information returned by the controller. 

Let’s take a peek behind the scenes, I’ve turned on the developer tools to see what happens when we enter data into the textbox and hit ‘Search’.
$Scope holds the value that was entered:


As well as the result returned by the SharePoint web service:



It also holds the variables that were created and placed into the fullLocEpp and fullLocINT variables.


Full Code

Here is the full code in all of its ‘copy and paste’-able glory.  AngularJS does not have much of a learning curve and if you really want to learn it, W3Schools introduction and coverage is very good. One thing to mention is that AngularJS 1.0 is very different from AngularJS 2.0.  I made the mistake of following some information from a forum on how to build my controller call only to have it repeatedly blow up.  After reverting back to W3Schools example, I realized that the version represented on the forum was version 1.0 not version 2.0, so be careful of that and stick with the W3Schools stuff, it is most up to date.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%@ Page Language="C#" %>
<%@ Register tagprefix="SharePoint" namespace="Microsoft.SharePoint.WebControls" assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<html>
<head runat="server">
<meta name="WebPartPageExpansion" content="full" />
<script type="text/javascript" src="http://xxx.sharepoint.com/.../ jsandjquerystuff/angular.min.js"></script>
<script src=" http://xxx.sharepoint.com/.../ jsandjquerystuff/jquery.js" type="text/javascript"></script>
<!--script type="text/javascript" src="welcomeBanner.js"></script-->
<title>Claims Acquisition Level Of Effort Tool 3.0</title>
</head>

<body>
<!-- (1) Create the controller -->
<div ng-app="myApp" ng-controller="myCtrl">
<!-- (2) Serves as the model. Binds prjID with whatever is provided in the input variable -->
<p>Project ID:<input type="text" ng-model="search"></p>
<button ng-click="myFunc()">Search</button>
<div>
Project Name: {{pname}} <a target="_blank" "id="int" ng-href="{{fullLocInt}}">Internal</a> <a target="_blank" id="epp" ng-href="{{fullLocEpp}}">EPP</a>
</div>
</div>

<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('myCtrl',
        function($scope, $http) //$scope binds HTML and JavaScript | $http: makes request to server and returns response.
        {
        $scope.myFunc=function()
        {
            $http({
                method: "GET",
                url:
" http://xxx.sharepoint.com/.../ _api/web/lists/getbytitle('PotentialProjectsLOE')/items?$filter=ProjectID eq '" + $scope.search + "'",
                headers: { "accept":"application/json;odata=verbose"}
                })//end http
                .then(
                function mysuccess(data, status, xhr)
                {
                        if (data.data.d.results.length==0)
                        {
                            alert("Please enter a VALID Project ID.");
                        }
                        else
                        {
                        $scope.pname=data.data.d.results[0].Title;
                        $scope.intid=data.data.d.results[0].ID;
                        $scope.eppid=data.data.d.results[0].EPPID;
                        $scope.fullLocInt="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/Lists/PotentialProjectsLOE/Item/displayifs.aspx?ID=" + $scope.intid;                       
                        $scope.fullLocEpp=" http://xxx.sharepoint.com/.../ Project/Lists/Project%20Status/item/displayifs.aspx?ID=" + $scope.eppid;
                        }
                },
                function epicfail(data, status, xhr)
                {
                    alert("Well...that sucked.  It didn't work.");
                });
        }
        });//end controller
</script>
</body>
</html>






No comments: