Wednesday, June 21, 2017

Using SharePoint for Web Content

Background

We needed a way to present information about one of our applications in a way that was easily accessible and fast.  I initially thought about using Wiki pages, but felt that something a little less static would be more fun.
I’ve used SharePoint to create content before, see ‘JQuery and SPServices’ and while the content was blogs, you can do the same thing using a common SharePoint list for your data source.

The Point

The List

I created a list with the following columns:


Column
Definition
Title
Servers as the Application category of content.  In this case, it is the name of the application.  Seeing the potential of the tool, I decided to put in this category so that we could expand it for multiple applications.
AppName
Serves as a functional view being represented by the narrative.
AppPic
Picture/Diagram associated with the functionality of the application.
AppDesc
The narrative describing the functionality of the application.
Category
Serves as the category grouping for the application, for instance Overview, Troubleshooting, Technical Details, etc.,
Source
Optional value that links to the document that is the source for your narrative (if applicable).
AppPic2
Sometimes more than one picture is necessary to tell the story.
Floater1/Floater2
When viewing the final page, these floaters appear over the corresponding pictures (AppPic, AppPic2).

The Content

Here is an example of the content of the list for one entry.  So the inevitable question is “Well…couldn’t you just send the user to this list?”.  Seriously??!!, where is the fun in that!?  Do you really want to point your users to something that looks so…well…SharePointy?  Besides, when you see what we do with JQuery in presenting this you’ll change your tune.


Setup

This app still uses SPServices for this as I have not converted over to SharePoint services yet.  So here are the usual suspects:
<title>CAFE OLE</title>
<!--JQuery UI CSS -->
<link rel="stylesheet" type="text/css" href="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/jsandjquerystuff/jquery-ui/jquery-ui.css"/>
<!--J Q U E R Y -->
<script type="text/javascript" src="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/jsandjquerystuff/jquery.js">
</script>
<!-- S P  S E R V I C E S -->
<script type="text/javascript" src="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/jsandjquerystuff/jquery.SPServices.js">   
</script>
<!--J Q U E R Y   U I -->
<script type="text/javascript"
src="https://bcbsnc.sharepoint.com/sites/global/ISBA_ProductDelivery/claimacq/jsandjquerystuff/jquery-ui/jquery-ui.js">
</script>


Get the App Info
function GetTops()
   {//--Begin Function
    //Get Site URL
    thissite = $().SPServices.SPGetCurrentSite();
    //Display this if on diagram is present
    nodoc = "#No Diagram";
    var query = "<Query>" +
    "<Where><Eq>" +
     "<FieldRef Name='Title'/><Value Type='Text'>CAFE</Value>" +
     "</Eq></Where>" +
    "<GroupBy>" +
      "<FieldRef Name='Category'/>" +
      "</GroupBy>" +
    "<OrderBy>" +
      "<FieldRef Name='Category'/>" +
      "</OrderBy>" +
    "</Query>";
                //The Web Service method we are calling, to read list items we use 'GetListItems'
                var method = "GetListItems";
               
                //Supply the location and name of the list we are reading data from
                var myWebURL = thissite;
                var list = "CA_APPS";

                //We need to identify the fields we want to return.
                var fieldsToRead ="<ViewFields>" +
                        "<FieldRef Name='ID' />" +
                        "<FieldRef Name='Title' />" +
                        "<FieldRef Name='APPNAME' />" +
                        "<FieldRef Name='APPPIC' />" +
                        "<FieldRef Name='APPDESC' />" +
                        "<FieldRef Name='Category' />" +
                        "<FieldRef Name='Source' />" +                       
                        "<FieldRef Name='Floater1' />" +                       
                        "</ViewFields>";
        //Here is our SPServices Call where we pass in the variables that we set above
        //This is where we show the Folder
        $().SPServices({
                debug: true,
                operation: method,
                async: false, 
                webURL: myWebURL,
                listName: list,
                CAMLViewFields: fieldsToRead,
                CAMLQuery: query,
                completefunc: function (xData, Status)
                {
                        var showmestuff = $().SPServices.SPDebugXMLHttpResult({ node:xData.responseXML });
                        //Uncomment the 2 lines below to show what is returned by the web service
                        //$("#debugMe").append(showmestuff);
                        //$("#debugMe").show("slow");
                        //this code iterates through every row of data returned from the web service call
                        $(xData.responseXML).SPFilterNode("z:row").each(function()
                       {                           

                            //get the ID’s                           
                            tabData.ID = ($(this).attr("ows_ID"));
                            //get the Groups
                            tabData.title = ($(this).attr("ows_Title"));
                            //get the Category
                            tabData.Cat = ($(this).attr("ows_Category"));
                            //get the App Name
                            tabData.aname = ($(this).attr("ows_APPNAME"));
                            //get the Diagrams
                            tabData.appic = ($(this).attr("ows_APPPIC"));
                            //get the Descriptions/Text
                            tabData.desc = ($(this).attr("ows_APPDESC"));
                            //get the source of the narrative
                            tabData.src = ($(this).attr("ows_Source"));                          
                            //get the Floater Text of the first graphic
                            tabData.f1 = ($(this).attr("ows_Floater1"));                           
                            epicpic = tabData.appic;
                            if (epicpic === undefined)
                                {
                                var showpic ="No Diagram";
                                }
                                else
                                {
//Pictures are stored with the description as a comma delimited field.  This will turn the field into an array
                                var arrpic = epicpic.split(",");
//This will take the 'picture' portion of the field and store it into field
                                var showpic = arrpic[0];
                                }
                                tabData.appic = showpic;
                             splithyper = tabData.src;
                                 if (splithyper == undefined)
                                 {
                                    tabData.src = "";
                                 }
                                 else
                                 {
                                     var splithyp = splithyper.split(",");
                                     tabData.src = splithyp[0];
                                     tabData.srcname = splithyp[1];
                                 }
                            //add the data from the row to the table on the screen
                            AddButtonsToDisplay(tabData);
                            AddRowToTable(tabData);
                      });//End SPFilterNode
                    var link2goback = "<a href=" + thissite + ">Return to SharePoint</a>";
                    $("#goHome").append(link2goback);                   
                   }//End  CompleteFunct
               });//--End SPServices
   } //End GetTopics

So for this SPServices call I’m using SPGetCurrentSite to get the location of the current site.
I’m hardcoding a variable called nodoc to “#No Diagram” in case there is not associated APPPIC.
Then I build the CAML Query, which in this case is pretty easy, I’m pulling all of the items in my list that have ‘CAFÉ’ in the Title and then grouping and ordering by Category.

Then I build my SPServices ‘GetListItems’ function by specifying:
·        The Method=GetListItems
·        The URL=myWebURL and setting it equal to the value I received from the previous SPService call to SPGetCurrentSite.
·        The List Name (list)=’CA_APPS’ (the name of the list)
·        The fields I want returned=fieldsToRead which are set to the columns in the CA_APPS list that I want returned.

And here is where the magic happens.  I plug in the values above into the respective SPServices parameters (highlighted in yellow).











I assign the values returned to an object that I call tabData. This way I can store multiple rows of data and display them when I want to.













I have to treat my returned data differently if there is not picture associated with it than I do if there is.



















I send the resulting tabData object to both AddButtonsToDisplay and AddRowToTable functions to build the page.

The Functions

AddButtonsToDisplay dynamically creates buttons on the screen.  AddRowToTable creates the rows of data and hides them until they are summoned by clicking on the dynamically created button. UnhideRow dramatically displays the data as requested (okay…it’s not that dramatic, but it’s still cool). UnhideDiv is responsible for creating the floater text and displaying it.
        function AddButtonsToDisplay(tabData)
        {
            showemall = "";
            if (holdCat != tabData.Cat)
            {
                showemall= "<div>" + tabData.Cat + "</div>";
            }
            var showemall = showemall + "<button id='"+ tabData.ID + "' type='button'>" + tabData.aname + "</button>";
            if (holdCat != tabData.Cat)
            {
                //showemall= showemall + "</div>";
                holdCat = tabData.Cat;
            }
            $("#listme").append(showemall);
            $("#shoButt").show("slow");
        }
Takes every ‘AppName’ returned and makes it a button with a unique ID. This ID is used to display the content, diagram, floaters, etc., when the corresponding button is pushed.
///// PLACE ALL DATA INTO HIDDEN TABLE ///////////
        function AddRowToTable(tabData)
        {
            if (tabData.appic=="No Diagram")
            {
                $("#postTable").append("<div id='" + tabData.ID + "' class='row'><div>" + tabData.aname + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'>No Image Provided</div></div><div id='"+ tabData.ID + "' class='row'><div id='" + tabData.ID + "' class='row'>" + tabData.desc + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><a href='" + tabData.src + "'>Source: Click here for more</a></div></div>");
            }
            else
            {
                $("#postTable").append("<div id='" + tabData.ID + "' class='row'><div class='col'>" + tabData.aname + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><img id='" + tabData.ID + "' class='grafic' src='" + tabData.appic + "'/></div></div><div id='" + tabData.ID + "' class='row'><div class='col'>" + tabData.desc + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><a href='" + tabData.src + "'>Source: Click here for more</a></div></div>");
                docarray[tabData.ID]=tabData.f1;
                namearray[tabData.ID]=tabData.aname;
                //$("#fh").append("<div class='fh'>" + tabData.aname + "</div>");
            }
        }

AddRowToTable takes all of the content, diagram, floaters, etc., and places it on the screen and hides it until it is requested through a button click.

postTable is a div with the ID=postTable. This is where we put all the hidden data.

The Result



As you may recall (or can scroll up to) one of our entries in the CA_APP table had an APPNAME of ‘CAFÉ Pharmacy Overview’.  This shows what happens with all of the entries with a Title of ‘Café’.  They are all returned and their APPNAME entries are made buttons.

The Button Handlers

        function unhideDiv(sendID, grphLoc)
        {
                $("#ShowMyText").offset(function()
                {
                    newPos=new Object();
                    newPos.left=grphLoc.left+500;
                    newPos.top=grphLoc.top;
                    return newPos;
                });
                text=docarray[sendID];
                if (text === undefined)
                {
                    text = namearray[sendID];
                }
                //alert("sendID=" + sendID);
                $("#ShowMyText").append("<div class='slider'>" + text + "</div>");
                $("#ShowMyText").show("slow"); //show the box
        }

unhideDiv is provided the sendID and the link to the picture that is eventually displayed.  When it is called it displays the div container with the corresponding data.
        function unhideRow(sendID)
        {
            var findme = "#" + sendID;
                $('.row').each (function()
                {
                    if ($(this).is(findme))
                        {
                            $(this).slideDown(3000); //show the row
                        }
                });
        }

unhideRow finds the hidden data given the sendID and slowly slides each row it into position.

So when we click on the Café Pharmacy Overview button, the Title, APPIC and Narrative are slowly revealed.


The Floater
And let’s not forget the floater.  When you hover over the diagram you see this:



The Full Monty

Well…that’s it.  While this code is copy and paste-able, I encourage you to take bits and pieces of it and tweak it to do different things.  Remember JQuery has many more animation options than just slidedown.  Play with it, have fun and learn something fun and new.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%@ Page Language="C#" %>
<!DOCTYPE HTML>
<html dir="ltr" xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<head runat="server">
<meta name="WebPartPageExpansion" content="full" />
<meta http-equiv="Content-Language" content="en-us" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9,chrome=1"/>
<title>CAFE OLE</title>
<!--JQuery UI CSS -->
<link rel="stylesheet" type="text/css" href="https://xx.sharepoint.com/sites/.../jsandjquerystuff/jquery-ui/jquery-ui.css"/>
<!--J Q U E R Y -->
<script type="text/javascript" src="https://xxx.sharepoint.com/sites/.../jsandjquerystuff/jquery.js">
</script>
<!-- S P  S E R V I C E S -->
<script type="text/javascript" src="https://xxx.sharepoint.com/sites/.../jsandjquerystuff/jquery.SPServices.js">   
</script>
<!--J Q U E R Y   U I -->
<script type="text/javascript"
src="https://xxx.sharepoint.com/sites/.../jsandjquerystuff/jquery-ui/jquery-ui.js">
</script>
<style type="text/css">
    .bigLetter { color:gray; letter-spacing:10px; font-family:Arial, Helvetica, sans-serif; display:none; font-size:medium}
    .myHeader { color:; letter-spacing:5px; font-family:Arial, Helvetica, sans-serif; font-size:large}
    .instructions {
    color:blue; font-family:Arial, Helvetica, sans-serif; font-size:x-small
}
    .topmarg { margin:50px }
    .boxHead {font-family:Arial, Helvetica, sans-serif; border: 2px solid orange;
    padding: 10px 40px; border-radius: 25px; display:none; width:500px}
    a:link {text-decoration:none;}
    a:visited {text-decoration:none;}
    a:hover {text-decoration:none;}
    a:active {text-decoration:none;}
    .floater {font-family:Arial, Helvetica, sans-serif; border: 2px solid black;
    padding: 10px 40px; border-radius: 25px; display:none; width:500px; background:yellow; z-index:2; float:left}
    .slider {width:200px; height:100px; text-align:left; float:left; color:#CC6600; border-style:groove; border-color:orange;background:#fdf5ce; font-family:Arial, Helvetica, sans-serif; font-size:small; border-radius: 25px; padding:5px;}
    .grafic {max-width:1200px;}
    .row {display:none}
    .flthdr {background:blue; color:white; font-size:small;font-weight:bold}
   
button
{
    width:200px;text-align:left;margin:5px;height:40px;background:#fdf5ce;color:#c77405;
}
tr
{
    display:none;
}
div.columns       { width: 1500px; }
div.columns div   { width: 300px; height: 100px; float: left; }

#butdiv
{
    float:left;
}

div.image {
    width:200px; text-align:left; background:maroon; color:yellow; z-index:2
}

</style>

<!--[if gte mso 9]>
<SharePoint:CTFieldRefs runat=server Prefix="mso:" FieldList="FileLeafRef,WikiField,_dlc_DocId,_dlc_DocIdUrl,_dlc_DocIdPersistId">
<xml>
<mso:CustomDocumentProperties>
<mso:_dlc_DocId msdt:dt="string">HSYJNS2YZDZT-3050-10</mso:_dlc_DocId>
<mso:_dlc_DocIdItemGuid msdt:dt="string">78812b48-5258-4db1-bd0a-9c226894295f</mso:_dlc_DocIdItemGuid>
<mso:_dlc_DocIdUrl msdt:dt="string">https://xxx.sharepoint.com/sites/.../_layouts/DocIdRedir.aspx?ID=HSYJNS2YZDZT-3050-10, HSYJNS2YZDZT-3050-10</mso:_dlc_DocIdUrl>
</mso:CustomDocumentProperties>
</xml></SharePoint:CTFieldRefs><![endif]-->
</head>

<body>
<!--Header-->
<div id="HEADER" class="bigLetter"></div>
<div><img height="50px" width="50px" alt="Pic of Coffee" src="https://xxx.sharepoint.com/sites/.../images/CafeOLE.jpg"/></div><div class="ui-state-hover">CAFE Topic Library</div>
<div id="instruct" class="instructions"></div>
<div id="floatme" class="floater"></div>
<div id="shoButt" class="boxHead">
    <div class="ui-state-hover">Topics</div>
    <div id="listme" class="colcont"></div>
</div>
<div id="postTable">
    <div id="goHome"></div>
</div>

<div id="ShowMyText" style="font-family:Arial, Helvetica, sans-serif"></div>


<script type="text/javascript">
/////////////////////////// G L O B A L    V A R I A B L E S ///////////////////////////////////////
thisUserGroup="";
thissite="";
docarray=new Array();
namearray=new Array()
docarr="";
howmany=0;
tabData = new Object();
dq = String.fromCharCode(34);
tab = String.fromCharCode(11);
holdCat="";
////////////////////////////// C O D E /////////////////////////////////////////////////////////////
        jQuery(document).ready(function($) {        //READY
            var thisUserName = $().SPServices.SPGetCurrentUser({
            fieldName: "Title",
            debug: false
        });
        ShowWelcome(thisUserName);
        });// End READY
        /********** Show the Welcome *************/
        function ShowWelcome(thisUserName)
        {
            $("#HEADER").append("<p>Welcome, " + thisUserName + "</p>");
            $("#HEADER").show();
            $("#instruct").show(); 
        GetTops();             
        }
//======================================================================================================
            function GetTops()
            {//--Begin Function
                //Get Site URL
                thissite = $().SPServices.SPGetCurrentSite();
                //Display this if on diagram is present
                nodoc = "#No Diagram";
                var query = "<Query>" +
                        "<Where><Eq>" +
                            "<FieldRef Name='Title'/><Value Type='Text'>CAFE</Value>" +
                        "</Eq></Where>" +
                        "<GroupBy>" +
                            "<FieldRef Name='Category'/>" +
                        "</GroupBy>" +
                        "<OrderBy>" +
                            "<FieldRef Name='Category'/>" +
                        "</OrderBy>" +
                    "</Query>";
                //The Web Service method we are calling, to read list items we use 'GetListItems'
                var method = "GetListItems";
               
                //Supply the location and name of the list we are reading data from
                var myWebURL = thissite;
                var list = "CA_APPS";

                //We need to identify the fields we want to return.
                var fieldsToRead ="<ViewFields>" +
                        "<FieldRef Name='ID' />" +
                        "<FieldRef Name='Title' />" +
                        "<FieldRef Name='APPNAME' />" +
                        "<FieldRef Name='APPPIC' />" +
                        "<FieldRef Name='APPDESC' />" +
                        "<FieldRef Name='Category' />" +
                        "<FieldRef Name='Source' />" +                       
                        "<FieldRef Name='Floater1' />" +                       
                        "</ViewFields>";
        //Here is our SPServices Call where we pass in the variables that we set above
        //This is where we show the Folder
        $().SPServices({
                debug: true,
                operation: method,
                async: false, 
                webURL: myWebURL,
                listName: list,
                CAMLViewFields: fieldsToRead,
                CAMLQuery: query,
                completefunc: function (xData, Status)
                {
                        var showmestuff = $().SPServices.SPDebugXMLHttpResult({ node:xData.responseXML });
                        //Uncomment the 2 lines below to show what is returned by the web service
                        //$("#debugMe").append(showmestuff);
                        //$("#debugMe").show("slow");
                        //this code iterates through every row of data returned from the web service call
                        $(xData.responseXML).SPFilterNode("z:row").each(function()
                       {                           

                            //get the Groups                          
                            tabData.ID = ($(this).attr("ows_ID"));
                            //get the Groups
                            tabData.title = ($(this).attr("ows_Title"));
                            //get the Category
                            tabData.Cat = ($(this).attr("ows_Category"));
                            //get the App Name
                            tabData.aname = ($(this).attr("ows_APPNAME"));
                            //get the Diagrams
                            tabData.appic = ($(this).attr("ows_APPPIC"));
                            //get the Descriptions/Text
                            tabData.desc = ($(this).attr("ows_APPDESC"));
                            //get the source of the narrative
                            tabData.src = ($(this).attr("ows_Source"));                          
                            //get the Floater Text of the first graphic
                            tabData.f1 = ($(this).attr("ows_Floater1"));                          
                            epicpic = tabData.appic;
                            if (epicpic === undefined)
                                {
                                var showpic ="No Diagram";
                                }
                                else
                                {
                                //Pictures are stored with the description as a comma delimited field.  This will turn the field into an array
                                var arrpic = epicpic.split(",");
                                //This will take the 'picture' portion of the field and store it into field
                                var showpic = arrpic[0];
                                }
                                tabData.appic = showpic;
                             splithyper = tabData.src;
                                 if (splithyper == undefined)
                                 {
                                    tabData.src = "";
                                 }
                                 else
                                 {
                                     var splithyp = splithyper.split(",");
                                     tabData.src = splithyp[0];
                                     tabData.srcname = splithyp[1];
                                 }
                            //add the data from the row to the table on the screen
                            AddButtonsToDisplay(tabData);
                            AddRowToTable(tabData);
                      });//End SPFilterNode
                    var link2goback = "<a href=" + thissite + ">Return to SharePoint</a>";
                    $("#goHome").append(link2goback);                   
                   }//End  CompleteFunct
               });//--End SPServices
   } //End GetTopics
//=======================================================================================================
        /////////PUT BUTTONS ON SCREEN //////////////
        function AddButtonsToDisplay(tabData)
        {
            showemall = "";
            if (holdCat != tabData.Cat)
            {
                showemall= "<div>" + tabData.Cat + "</div>";
            }
            var showemall = showemall + "<button id='"+ tabData.ID + "' type='button'>" + tabData.aname + "</button>";
            if (holdCat != tabData.Cat)
            {
                //showemall= showemall + "</div>";
                holdCat = tabData.Cat;
            }
            $("#listme").append(showemall);
            $("#shoButt").show("slow");
        }
        ///// PLACE ALL DATA INTO HIDDEN TABLE ///////////
        function AddRowToTable(tabData)
        {
            if (tabData.appic=="No Diagram")
            {
                $("#postTable").append("<div id='" + tabData.ID + "' class='row'><div>" + tabData.aname + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'>No Image Provided</div></div><div id='"+ tabData.ID + "' class='row'><div id='" + tabData.ID + "' class='row'>" + tabData.desc + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><a href='" + tabData.src + "'>Source: Click here for more</a></div></div>");
            }
            else
            {
                $("#postTable").append("<div id='" + tabData.ID + "' class='row'><div class='col'>" + tabData.aname + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><img id='" + tabData.ID + "' class='grafic' src='" + tabData.appic + "'/></div></div><div id='" + tabData.ID + "' class='row'><div class='col'>" + tabData.desc + "</div></div><div id='" + tabData.ID + "' class='row'><div class='col'><a href='" + tabData.src + "'>Source: Click here for more</a></div></div>");
                docarray[tabData.ID]=tabData.f1;
                namearray[tabData.ID]=tabData.aname;
                //$("#fh").append("<div class='fh'>" + tabData.aname + "</div>");
            }
        }
        ////////// WHEN SOMEONE CLICKS A BUTTON ////////////////////////////////////////////////////////////////////////////
        //////// 1. DETERMINE THE ROW ID'S THAT MATCH THE BUTTON ID'S
        ////////    a. Have to use the ("button").live("click", function()... because the buttons were created dynamically
        //////// 2. FIND THE ROWS
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        $("button").live("click",function()
         {
            $(".row").hide();
            var sendID=this.id;
            //alert("button id=" + sendID);
            unhideRow(sendID);
        });
        //////////////////// UNHIDE ONLY THOSE ROWS ASSOCIATED WITH THE BUTTON CLICKED ////////////////////////////////
        function unhideRow(sendID)
        {
            var findme = "#" + sendID;
                $('.row').each (function()
                {
                    if ($(this).is(findme))
                        {
                            $(this).slideDown(3000); //show the row
                        }
                });
        }
        ////////////////////////SHOW BRIEF DESCRIPTION WHEN SOMEONE CLICKS THE IMAGE/////////////////////////////
        //////// 1. Determine the ID
        //////// 2. Find the floater text that corresponds to the ID
        //////// 3. Determine the position to place the floating box
        //////// 4. Display the floating box
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        $("img").live("mouseover", function()
        {
            //$("div").hide();
            var sendID=this.id;
            grphLoc=$(this).offset();
            unhideDiv(sendID, grphLoc);
        });

        function unhideDiv(sendID, grphLoc)
        {
                $("#ShowMyText").offset(function()
                {
                    newPos=new Object();
                    newPos.left=grphLoc.left+500;
                    newPos.top=grphLoc.top;
                    return newPos;
                });
                text=docarray[sendID];
                if (text === undefined)
                {
                    text = namearray[sendID];
                }
                //alert("sendID=" + sendID);
                $("#ShowMyText").append("<div class='slider'>" + text + "</div>");
                $("#ShowMyText").show("slow"); //show the box
        }

        // Move the mouse away from the graphic, hide the floating box
        $("img").live("mouseout", function()
        {
            $(".slider").hide();
        });
</script>
&nbsp;
<div id="debugMe">
</div>

</body>

</html>



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>