SharePoint / Software Development / JavaScript

Creating SharePoint solutions with Typescript

Kwiecień 21, 2016 0
Podziel się:

This article will show you how to use typescript (http://www.typescriptlang.org/ ) in your SharePoint solution in example of SharePoint-hosted app.

Introduction

We have a project, which has a requirement to create solution compatible with SharePoint-online as well as SharePoint on-premises. The challenging part was in the Deployment of the solution to SPWeb.

To implement customer requirements we chose SharePoint-hosted app with possibility for auto-deployment of the whole infrastructure such as Content Types, Lists, Images, Webparts and scripts using JSOM API. Due to the fact that project contains few independent parts with the similar deployment requirements we decided to create a common reusable API. The best choice for this purpose was Typescript, which is strongly typed language with interfaces and extensible classes. The good point is the Typescript is compiled into Readable and Debuggable JavaScript code.

The Typescripts files have .ts extensions and are compiled into .js and .map. The .map file has an exact line mapping of Typescript line of code in correlation to JavaScript line of code. This mapping information allows to debug Typescript using Visual Studio.

ts_1

The generated files are not added automatically into solution.
You need to configure Visual Studio in order to compile Typescript. Follow the steps below:

  1. Install Typescript extension for Visual Studio from official site
  2. Good news for developers – ReSharper support IntelliSense for Typescript. Please read details here
  3. Create and configure Sharepoint-hosted app project in Visual Studio:
    • Create SharePoint-hosted app
    • Add NuGet package to a project: Microsoft.TypeScript.MSBuild
  4. Add NuGets with Typescript definitions. The key word for Typescript definitions is “definitelytyped”, for example, the search result for sharepoint definitelytyped is:

ts_2
The same way you can find jquery definitelytyped Typescript definition. The result of the nuget installation is folder with “typings” name which contains package subfolder with an *.d.ts extension file.
ts_3
After this configuration, Visual Studio would be able to compile typescripts and it will add new
“Typescript” tab to project properties.
Let’s create simple project using Typescript to provision Content Types to Site Collection. We need four files to do it (the same as in C# good solution is to separate classes and responsibilities per file):

  1. ProvisionDefinition.ts – the static module which defines provisioning rules (contentype names, list names, fields, file locations).
  2. ContentTypeModel.ts – object with the list of properties defining conten type
  3. DeploymentTool.ts – the “service” wrapping the sharepoint API
  4. Main.ts – the main module represents the interface for UI

After the adding of files and rebuilding of the project click the “Show all files” button in solution window of Visual Studio:
ts_4
As a result, you will see the list of compiled files next to created .ts files. Please add all the .js files into solution.
The next step is to define content types and its properties. Below you could find the content type definition TS file and its compiled JS:
ts_5
The definition of Missing ContentTypeModel class is very simple: just a constructor and the list of simple properties:

class ContentTypeModel {
    constructor(options) {
        this.Name = options.Name;
        this.Group = options.Group;
        this.Description = options.Description;
        this.Inherits = options.Inherits;
        this.Version = options.Version;
        this.ParentName = options.ParentName;
    }

    public Name: string;
    public Group: string;
    public Description: string;
    public Inherits: string;
    public Version: string;
    public ParentName: string;
}

The definition of Service class DeploymentTool has a strong type constructor and a couple of methods:

class DeploymentTool {
    private web: SP.Web;
    private clientContext: SP.ClientContext;


    constructor(clientContext: SP.ClientContext, web: SP.Web) {
         this.web = web;
         this.clientContext = clientContext;
    }

    public createContentTypes(contentTypesDefinitions: Array<ContentTypeModel>, done: any ): void {
        try {
            for (var i = 0; i < contentTypesDefinitions.length; i++) { this.createContentType(contentTypesDefinitions[i], (contentType: SP.ContentType, error: string) => {
                    if(contentType != null)
                    { console.log("Content type with name: " + contentType.get_name() + " and ID: " + contentType.get_stringId() + " has been created"); }
                    if (error != null) {
                        console.log(error);
                    }
                });
            }
        } catch (ex) {
            done(ex);
        }
        done(null);
    }

    private getContentTypeByName(contentTypeCol, ctName) {
        var myCt = null;
        var contentTypeEnumerator = contentTypeCol.getEnumerator();
        while (contentTypeEnumerator.moveNext()) {
            var content = contentTypeEnumerator.get_current();
            if (content.get_name() === ctName) {
                myCt = content;
                break;
            }
        }
        return myCt;
    }

    private createContentType(contentTypesDefinition: ContentTypeModel, done: any): void {
        var webContentTypes = this.web.get_contentTypes();
        var these = this;
        these.clientContext.load(webContentTypes);
        these.clientContext.executeQueryAsync(() => {
            var current = these.getContentTypeByName(webContentTypes, contentTypesDefinition.Name);
            if (current != null) {
                done(current);
                return;
            }
            var newContentType = new SP.ContentTypeCreationInformation();
            newContentType.set_name( contentTypesDefinition.Name);
            newContentType.set_description( contentTypesDefinition.Description);
            newContentType.set_group( contentTypesDefinition.Group);
            var parent = these.getContentTypeByName( webContentTypes, contentTypesDefinition.ParentName);
            newContentType.set_parentContentType(parent);
            current = webContentTypes.add(newContentType);
            these.clientContext.load(current);
            these.clientContext.load(webContentTypes);
            these.clientContext.executeQueryAsync(() => {
                done(current, null);
            }, (sender, args) => {
                done(null, args.get_message());
            });
        }, () => {
            console.log(contentTypesDefinition.Name + " provision error");
        });
    }
}

The signature of the most important method here is createContentType(contentTypesDefinition: ContentTypeModel, done: any): void and it means that we have first parameter strongly typed ContentTypeModel and the second one delegate to post back event called done. The method itself returns void. Luckily included sharepoint.d.ts definition file contains all the SP.ContentTypeCreationInformation object methods and intellisence help us to define new ContenType:
ts_6
And the couple of last steps to create simple UI and newly created API interaction. Add JS links into default.aspx page

<script type="text/javascript" src="../Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="../Scripts/AppScripts/ContentTypeModel.js"></script>
<script type="text/javascript" src="../Scripts/AppScripts/DeploymentTool.js"></script>
<script type="text/javascript" src="../Scripts/AppScripts/ProvisionDefinition.js"></script>
<script type="text/javascript" src="../Scripts/AppScripts/Main.js"></script>

Define the button to start deployment:



<div>
    <input type="button" value="Provision content types" class="main-btn" style="display: none" id="provisionContentTypes" onclick="UiFunctions.ButtonClicks.contentTypeProvision()"/>
</div>



And the main.ts file will contain “ondocumentready” event with loading of all required sharepoint scripts and module definition for event functions:

var _deploymentTool;
$(document).ready(() => {
    var scriptbase = _spPageContextInfo.layoutsUrl + "/";
    $.getScript(scriptbase + "SP.Runtime.js", () => {
        $.getScript(scriptbase + "SP.js", () => {
            var context = new SP.ClientContext(_spPageContextInfo.siteServerRelativeUrl);
            var web = context.get_web();
            context.load(web);
            context.executeQueryAsync(() => {
                _deploymentTool = new DeploymentTool(context, web);
                $("#provisionContentTypes").show();
            }, () => {
                console.log("SP.Web initialization failed");
            });
        });
    });
});


module UiFunctions {
    export module ButtonClicks {
        export function contentTypeProvision() {
            
            _deploymentTool.createContentTypes( Deployment.ContentTypes, () => {
                console.log("content type provisioning");
            });
        }
    }
}

The difference between module and class is that the module can play the same role as a C# static context.

Conclusion

So, the summary is simple – typescript allows to create client side code with normal object oriented structure, plus we finally have a possibility to see the intellisence for SharePoint, angular, knockout, jQuery and other APIs. Thanks to all people who worked on it.
Useful links:

  1. http://www.typescriptlang.org/ – Typescript homepage
  2. https://github.com/gandjustas/sptypescript – SharePoint typescript homepage
  3. https://github.com/DefinitelyTyped/DefinitelyTyped a LOT of definitely typed libraries
Oceń ten post
Sergey Shutov
Autor: Sergey Shutov
SharePoint warrior for more than 7 years

Imię i nazwisko (wymagane)

Adres email (wymagane)

Temat

Treść wiadomości

Zostaw komentarz