Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An End-to-End AngularJS SharePoint Module using LightSwitch OData as a Back-End

0.00/5 (No votes)
13 Oct 2014 1  
You can create SharePoint Provider Hosted applications using AngularJS and Visual Studio LightSwitch. This will allow you to create applications faster and easier.

image

You can create SharePoint Provider Hosted applications using AngularJS and Visual Studio LightSwitch. This will allow you to create applications faster and easier.

image

The application we will create in this article will display a list of Tasks, as well as the user who created the Task. To edit an existing Task, we click on the Task. To create a new Task, we click the add new button.

image

To save a new Task or edit an existing Task, we enter the data and click the Save button. If we wanted to delete an existing Task, we would click the Remove button.

image

The application also demonstrates how to implement business rules and security.

Why Would You Want to Use AngularJS with LightSwitch?

  1. You need 100% control over the UI – The one disadvantage the LightSwitch HTML Client has is its dependence on JQuery Mobile. This is fine in most cases but you would have a hard time matching a pixel by pixel design specification using it.
  2. You need to implement security – Security must be implemented on the server-side. You cannot implement security inside AngularJS because it is a client-side technology. LightSwitch makes security easy as we will demonstrate in this example.
  3. You want to use LightSwitch for the Administration screensAngularJS screens take a lot longer to create than a standard LightSwitch screen. However, there is no need to code all the screens in your application in AngularJS. As this example will demonstrate, you can mix AngularJS and LightSwitch screens in the same application.
  4. You want to build a OData service layer for use with other applications – LightSwitch is the easiest and most robust way to create an OData service.
  5. You want to speed up development – There is a lot less code to implement when you use LightSwitch as your back-end. This means faster development and fewer bugs.

Using Web API vs. Calling OData Directly

You can use LightSwitch as a back-end for AngularJS using Web API as demonstrated in the article, Creating an AngularJS CRUD Application Using Visual Studio LightSwitch. The structure looks like this:

image

However, you can consume your backend OData Visual Studio LightSwitch services in directly in AngularJS using JayData (as demonstrated in the article: Using JayData to Consume the Visual Studio LightSwitch OData Business Layer in a AngularJS CRUD Application). This will save you a lot of development time and reduce the code you will be required to write. The structure looks like this:

image

In the example in this article, we will call the LightSwitch OData service layer directly.

Create the Project

image

In Visual Studio 2013, we create a New Project.

image

We create a Cloud Business App.

image

We will be prompted to enter a SharePoint development site.

image

Click Finish.

image

The project will be created.

image

The Solution will display.

Add Data Source and Business Logic

image

In the Server project, right-click on the Data Sources folder and select Add Table.

image

Create the ToDo table.

image

In the Write Code menu, select the Validate method.

Use the following code for the method:

partial void ToDoes_Validate(ToDo entity, EntitySetValidationResultsBuilder results)
{
    if (entity.Details.EntityState == EntityState.Modified)
    {
        if (entity.CreatedByInfo.Id != this.Application.User.Id)
        {
            results.AddEntityError(
            "Task can only be modified by " + entity.CreatedByInfo.Identity.Name
            );
        }
    }

    // Do not allow a task to be called {New Task]
    if (entity.TaskName == "[New Task]")
    {
        results.AddEntityError(
            "Task cannot be named [New Task]"
            );
    }

    // Do not allow more than 1 incomplete Task
    if (entity.IsComplete == false)
    {
        int intCountOfIncomplete =
            this.DataWorkspace.ApplicationData.ToDoes
            .Where(x => x.IsComplete == false).Count();

        if (intCountOfIncomplete > 0)
        {
            results.AddEntityError(
                "Cannot have more than 1 incomplete Task"
                );
        }
    }

image

Return to the table designer, and in the Write Code menu, select the Deleting method.

Use the following code for the method:

partial void ToDoes_Deleting(ToDo entity)
{
    if (entity.CreatedByInfo.Id != this.Application.User.Id)
    {
        throw new ValidationException("Task can only be deleted by "
            + entity.CreatedByInfo.Identity.Name);
    }
}

Create the LightSwitch HTML Client Screens

image

In the HTMLClient project, right-click on the Screens folder and select Add Screen.

image

Use the Common Screen Set template to create screens for the ToDo entity.

image

The screens will be created.

image

Run the project.

image

You will be required to log into your SharePoint developer site.

image

The application will be side-loaded into your SharePoint development site.

image

Your web browser will open and you be required to log into your SharePoint developer site.

image

You will be required to Trust the application.

image

The application will load and you will have the ability to add and edit To Do items.

You will note that the validation and business rules are enforced.

Close the web browser and return to Visual Studio.

Create the AngularJS Application

image

Right-click on the Server project and select Manage NuGet Packages.

image

Install AngularJS.

image

Install JayData.

image

Create a folder in the Server project called Pages.

Right-click on the Pages folder and add a new JavaScript file.

image

Name the file JayDataCRUD.

Add the following code to the file to create the Angular App:

(function () {
    "use strict";
    // Create a app and inject jaydata
    var app = angular.module('app', ['jaydata']);
})();

Add the controller code to the file:

(function () {
    // Create a ToDoEditorController injecting $scope and $data (for JayData)
    angular.module('app')
        .controller('ToDoEditorController', ['$scope', '$data',
            function ToDoEditorController($scope, $data) {
                // Create an empty ToDoes collection
                $scope.ToDoes = [];
                // Set the selectedToDo to nothing for now
                $scope.selectedToDo = null;

                // Call the /ApplicationData.svc LightSwitch OData service
                $data.initService('/ApplicationData.svc').then(function (ApplicationData) {
                    $scope.ApplicationData = ApplicationData;
                    $scope.ToDoes = ApplicationData.ToDoes.toLiveArray();
                });

                // This will be called when the collection changes
                Object.defineProperty($scope, "colToDoes", {
                    get: function () {
                        return $scope.ApplicationData
                          .ToDoes
                          .toLiveArray();
                    }
                });

                $scope.save = function () {
                    if ($scope.selectedToDo.Id) {
                        // Save an existing ToDo item
                        $scope.ApplicationData.ToDoes.attach($scope.selectedToDo, true);
                        $scope.selectedToDo.entityState = $data.EntityState.Modified;
                    }
                    else {
                        // Save a new ToDo item
                        $scope.ApplicationData.ToDoes.add($scope.selectedToDo, true);
                    }
                    $scope.saveChanges();
                };

                // Save any changes
                $scope.saveChanges = function () {
                    $scope.ApplicationData.saveChanges()
                    .then(function () {
                        $scope.selectedToDo = null;
                    }, function (error) {
                        // Get the validation error messages from LightSwitch
                        var xml = error.message,
                        xmlDoc = $.parseXML(xml),
                        $xml = $(xmlDoc),
                        $ValidationResults = $xml.find("ValidationResults");

                        if ($ValidationResults[0].childNodes.length == 0) {
                            // This is a simple error
                            $MessageError = $xml.find("Message")[0].childNodes['0'].data;
                            alert($MessageError);
                        } else {
                            // This is a entity validation error
                            angular.forEach($ValidationResults, function (ValidationResult) {
                                angular.forEach
                                    (ValidationResult.childNodes, function (childNode) {
                                    alert(childNode.childNodes[0].textContent);
                                });
                            });
                        }

                        $scope.ApplicationData.stateManager.reset();
                    });
                };

                $scope.remove = function () {
                    // Remove the ToDo item
                    $scope.ApplicationData.ToDoes.remove($scope.selectedToDo);
                    $scope.saveChanges();
                };

                $scope.newToDo = function () {
                    var ctx = $scope.ApplicationData;
                    // Add a new ToDo item
                    $scope.selectedToDo = new ctx.ToDoes.elementType({
                        // Set the default value for the Task Name
                        TaskName: "[New Task]"
                    });
                };
            }]);
})();

image

Right-click on the Pages folder and add a new Web Form file.

(You could use a normal .html page but you may have to configure your local IIS Express to run the page in this context so we are just avoiding the issue by using a Web Form page. You can also use a MVC page, but again we are avoiding the extra steps to enable MVC in the project.)

image

Name the file JayDataCRUD.

image

The file will be created.

Change the code in the markup to the following:

<%@ Page Language="C#"

    AutoEventWireup="true"

    CodeBehind="JayDataCRUD.aspx.cs"

    Inherits="LightSwitchApplication.Pages.JayDataCRUD" %>

<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE-8" />
    <script src="../Scripts/jquery-1.8.0.js"></script>
    <script src="../Scripts/angular.js"></script>
    <script src="../Scripts/datajs-1.0.3.js"></script>
    <script src="../Scripts/jaydata.js"></script>
    <script src="../Scripts/jaydatamodules/angular.js"></script>    
    <script src="https://ajax.aspnetcdn.com/ajax/4.0/1/MicrosoftAjax.js"

        type="text/javascript"></script>
    <script src="JayDataCRUD.js"></script>
    <title>JayDataCRUD</title>
</head>
<body ng-cloak class="ng-cloak" data-ng-app="app">
    <!-- Chrome control placeholder -->
    <div id="chrome_ctrl_placeholder"></div>
    <div id="MainContent" data-ng-controller="ToDoEditorController">
        <table>
            <tr data-ng-repeat="ToDo in colToDoes">
                <td>
                    <input id="checkbox" type="checkbox"

                        data-ng-model="ToDo.IsComplete" disabled="disabled"></td>
                <td>
                    <a href="#"

                        data-ng-click="$parent.selectedToDo = ToDo">{{ToDo.TaskName}}</a>
                </td>
                <td>[{{ToDo.CreatedBy}}]</td>
            </tr>
        </table>
        <p>
            <button class="button normaltext" data-ng-click="newToDo()">add new</button></p>
        <form data-ng-if="selectedToDo">
            <fieldset style="width: 300px;">
                <legend>{{selectedToDo.TaskName}}</legend>
                <br />
                <label>
                    <span><strong>Id:</strong></span>
                    <span>{{selectedToDo.Id}}</span>
                    <span>
                        <br />
                        <strong>Task Name:</strong>
                    </span>
                    <input data-ng-model="selectedToDo.TaskName" size="20" />
                    <span>
                        <br />
                        <strong>Is Complete:</strong>
                    </span>
                    <input type="checkbox" data-ng-model="selectedToDo.IsComplete" />
                    <br />
                    <br />
                </label>
                <button data-ng-click="save()">Save</button>
                <button data-ng-click="remove()">Remove</button>
            </fieldset>
        </form>
    </div>
</body>
</html>

Configure the AngularJS Page to Load

image

In the SharePoint project, double-click on the AppManifest.xml file to open it.

image

Select the JayDataCRUD.aspx page as the Start Page.

Run the AngularJS Application

image

When we run the application, we will have to Trust it again (because we modified the SharePoint configuration for the application).

image

The AngularJS application will load.

image

The security and business rules will be enforced.

Match the Style of the SharePoint Site

To style the application to match the SharePoint site, we add the following to the JayDataCRUD.aspx file:

<script type="text/javascript">
    // Set the style of the client web part page
    // to be consistent with the host web.
    (function () {
        'use strict';

        var hostUrl = '';
        if (document.URL.indexOf('?') != -1) {
            var params = document.URL.split('?')[1].split('&');
            for (var i = 0; i < params.length; i++) {
                var p = decodeURIComponent(params[i]);
                if (/^SPHostUrl=/i.test(p)) {
                    hostUrl = p.split('=')[1];
                    document.write('<link rel="stylesheet" href="'
                        + hostUrl + '/_layouts/15/defaultcss.ashx" />');
                    break;
                }
            }
        }
        if (hostUrl == '') {
            document.write('<link rel="stylesheet"'
                + 'href="/_layouts/15/1033/styles/themable/corev15.css" />');
        }
    })();
</script>

image

Now when we run the application, the styles match.

Add the SharePoint Chrome (Logo and Toolbar)

To create a SharePoint Chrome (the title bar, logo, help icon and a “gear” icon that contains links to other pages in the application), we add a JavaScript file called ChromeLoader.js to the Scripts directory (and add a reference to it in the JayDataCRUD.aspx file).

We use the following code for the file:

var hostweburl;

//load the SharePoint resources 
$(document).ready(function () {
    //Get the URI decoded URL. 
    hostweburl =
        decodeURIComponent(
            getQueryStringParameter("SPHostUrl")
    );

    // The SharePoint js files URL are in the form: 
    // web_url/_layouts/15/resource 
    var scriptbase = hostweburl + "/_layouts/15/";

    // Load the js file and continue to the  
    //   success handler 
    $.getScript(scriptbase + "SP.UI.Controls.js", renderChrome)
});

//Function to prepare the options and render the control 
function renderChrome() {

    // The Help, Account and Contact pages receive the  
    //   same query string parameters as the main page 
    var options = {
        "appIconUrl": hostweburl + "/_layouts/15/images/siteicon.png",
        "appTitle": "AngularJS CRUD App",
        "appHelpPageUrl": "JayDataCRUD.aspx?"
            + document.URL.split("?")[1],
        "settingsLinks": [
            {
                "linkUrl": "../HTMLClient/default.htm?"
                    + document.URL.split("?")[1],
                "displayName": "LightSwitch Application"
            }
        ]
    };

    var nav = new SP.UI.Controls.Navigation(
                            "chrome_ctrl_placeholder",
                            options
                        );
    nav.setVisible(true);
}

// Function to retrieve a query string value. 
function getQueryStringParameter(paramToRetrieve) {
    var params =
        document.URL.split("?")[1].split("&");
    var strParams = "";
    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == paramToRetrieve)
            return singleParam[1];
    }
}

image

The application is now complete.

image

Clicking the “gear” icon will display a link to take us to the LightSwitch pages we created earlier.

Publishing and Deploying

image

Even though we have added AngularJS added to the project, it is still a LightSwitch project. Therefore to deploy, follow the directions here:

Links – Microsoft

Links – JayData

Links – LightSwitch Help Website

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here