Getting started with SAPUI5

There are some great HTML5 UI frameworks available in the marketplace: Sencha Touch, KendoUI and Twitter Bootstrap to name just a few. And now SAP has unveiled the Beta version of SAPUI5, officially known as “UI development toolkit for HTML5”.

As a follow up on my post on Building a SAP mobile app with Sencha Touch 2, I was keen on having a hands-on with SAPUI5. These helpful posts helped me to get started.

In this post we’ll set up the Beta version of SAPUI5 for use in Visual Studio and create a demo SAPUI5 app that fetches SAP ERP Sales Orders from SAP Gateway and combines them with issues tracked in a SharePoint list.

Installing the SAPUI5 SDK on Visual Studio 2010

  1. Visit the homepage for SAPUI5 on SDN and download the trial version (SAPUI5 beta)
  2. Unzip the downloaded file HTML5Beta_complete.zip
  3. Open the folder with extracted content. Unzip ‘sapui5-static.zip’. You will need the ‘resources’ folder for development
  4. Rename demokit.war to demokit.zip and unzip. This archive contains the documentation which you can install locally.
  5. In Visual Studio, create a solution and within the solution ‘Add a new web site’
  6. Copy the contents of the demokit into the web site
  7. Copy the resource folder of sapui5-static.zip to the web site
  8. Rename index.html to default.html (the VS dev server opens default.html instead of index.html when browsing a directory).
  9. Your file/folder structure should now look like this:image
  10. Now select the web site and ‘View in Browser’

You now have access to the documentation of SAPUI5:

SNAGHTML379228f

A first test

Now we have the documentation up and running, we create our first test to see if we have all the required files.

To run this test, setup a new project or website in VS. Copy the ‘resources’ folder from sapui5-static into the site and create the following test.html file:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv='X-UA-Compatible' content='IE=edge'>
        <title>SAPUI5 test</title>
        <script id="sap-ui-bootstrap"
           src="resources/sap-ui-core.js"
           data-sap-ui-theme="sap_platinum"
           data-sap-ui-libs="sap.ui.commons"&gt;&lt;/script&gt;
        <script>
            $(function () {
                // show an SAPUI5 alert box
                sap.ui.commons.MessageBox.alert("SAPUI5 ready for action.");
            })
        </script>
    </head>
    <body>
    </body>
</html>

The test.html file instructs the browser to load the sap-ui-core.js file which contains jQuery and also a dynamic loader which will load further required js and css files. To show that everything loads correctly, we simply create an alert box (one of the SAPUI5 controls) when the DOM is ready.

View the test.html file in the browser and you see:

SNAGHTML7a1fad9

OK, we’re all set to do something a little more exciting!

Building a demo app

Consider the following business scenario: a company is managing Sales Orders in SAP ERP. To improve order fulfillment, they use a issue tracking workflow system in SharePoint. Their requirement is to see the info from SAP and SharePoint in a single screen. Let’s build that with SAPUI5!

We will connect to the online SAP Gateway demo system to fetch Sales Orders from SAP ERP. Using the demo SAP Gateway system, you can try out SAPUI5 without having to install/configure any server side components.

Our issue tracking list is part of a SharePoint Online site (SharePoint Online is part of Microsoft’s Office365 offering). Each Issue contains a field Sales Order ID which we will use to filter. Here’s how the Issues look inside the SharePoint site:

image

Both SAP Gateway and SharePoint are OData providers, so we will use SAPUI5’s OData model to connect to the backend systems and parse the responses. Our application page will present three tables:

  • Sales Orders: the master table showing the available Sales Orders
  • Line items: a details table showing the line items belonging to the selected Sales Order
  • Issues: a details table showing the issues tracked in SharePoint related to the selected Sales Order.

This structure is already pre-defined in the body of the index.html file:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv='X-UA-Compatible' content='IE=edge'>
        <title>SAPUI5 demo</title>
        <script id="sap-ui-bootstrap"
           src="/resources/sap-ui-core.js"
           data-sap-ui-theme="sap_platinum"
           data-sap-ui-libs="sap.ui.commons, sap.ui.table"></script>
        <script src="app/app.js"></script>
    </head>
    <body class="sapUiBody">
         <img src="images/sap_logo.png" >
         <div id="salesorders"></div>
         <div id="lineitems"></div>
         <img src="images/o365_logo.jpg" style="margin:30px 0 10px 0" />
         <div id="issues"></div>
    </body>
</html>

The JavaScript of our application is contained in app/app.js.

// Let's define some shortcuts to increase
// readability of the code
var ODataModel = sap.ui.model.odata.ODataModel,
    TextField = sap.ui.commons.TextField,
    TextView = sap.ui.commons.TextView,
    Label = sap.ui.commons.Label,
    DataTable = sap.ui.table.DataTable,
    Column = sap.ui.table.Column,
    SelectionMode = sap.ui.table.SelectionMode;
// Specify the SAP Gateway SalesOrder service as an OData model
var salesOrderService =
        "https://gw.esworkplace.sap.com/sap/opu/sdata/IWFND/SALESORDER",
    // The SalesOrder service requires authentication
    // get the username/password from the SDN page.
    username = "[username]",
    password = "[password]",
    // SAP Gateway only supports XML, so don't use JSON
    asJson = false,
    salesOrderModel = new ODataModel(salesOrderService, asJson, username, password)
    salesOrderCollection = "SalesOrderCollection";
// specify the SharePoint site containing the Sales Order Issues as an OData model
// we will assume there a SharePoint site called 'demo'
// which has an issues list called 'SalesOrderIssues'
var issueService =
        "http://[SharePoint_server]/demo/_vti_bin/ListData.svc",
    issueCollection = "SalesOrderIssues"  // name of SP List
    issueModel = new ODataModel(issueService);
// Create a master table with sales orders
var salesOrders = new DataTable({
    title: "Sales Orders",
    width: "100%",
    visibleRowCount: 5,
    selectionMode: SelectionMode.Single,
    editable: false
});
// define the relevant column properties
var salesOrderColumns = [
    { header: "Sales Order ID", value: "{SalesOrderID}", width: '100px' },
    { header: "Customer Name", value: "{CustomerName}", width: '50%' },
    { header: "Amount", value: "{TotalSum}", width: '50%' }
];
// create the columns
salesOrderColumns.forEach(function (column) {
    var label = new Label({ text: column.header }),
        template = new TextView({ text: column.value }),
        column = new Column({
            label: label,
            template: template,
            width: column.width
        });
    salesOrders.addColumn(column);
});
// connect the data table to the SalesOrder service
salesOrders.setModel(salesOrderModel);
// An OData request for the SalesOrderCollection
// will return the sales orders.
// Each sales order should result in a table row.
salesOrders.bindRows(salesOrderCollection);
// Put table in the DOM.
// placeAt will automatically defer if
// DOM is not ready yet (like in this demo).
salesOrders.placeAt("salesorders");
// At this point the Sales Order master table is done
// Creating the lineItems and issues table is very similar
// Creating the line items datatable
var lineItems = new DataTable({
    title: "Line items",
    width: "100%",
    visibleRowCount: 10,
    selectionMode: SelectionMode.Single,
    editable: false
});
lineItemColumns = [
    { header: "Line item #", value: "{SalesOrderItem}", width: '100px' },
    { header: "Product Name", value: "{ProductName}", width: '50%' },
    { header: "Amount", value: "{NetSum}", width: '50%' }
]
lineItemColumns.forEach(function (column) {
    var label = new Label({ text: column.header }),
    template = new TextView({ text: column.value }),
    column = new Column({
        label: label,
        template: template,
        width: column.width
    });
    lineItems.addColumn(column);
})
lineItems.setModel(salesOrderModel);
lineItems.placeAt("lineitems");
// Create the issues datatable
var issues = new DataTable({
    title: "Issues",
    width: "100%",
    visibleRowCount: 5,
    selectionMode: SelectionMode.Single,
    editable: false
});
issuesColumns = [
    { header: "Id", value: "{Id}", width: '30px' },
    { header: "Title", value: "{Title}", width: '40%' },
    { header: "Status", value: "{IssueStatusValue}", width: '10%' },
    { header: "Comments", value: "{Comments}", width: '50%' }
]
issuesColumns.forEach(function (column) {
    var label = new Label({ text: column.header }),
        template = new TextView({ text: column.value }),
        column = new Column({
            label: label,
            template: template,
            width: column.width
        });
    issues.addColumn(column);
});
issues.setModel(issueModel);
issues.placeAt("issues");
// The three data tables are ready!
// Now we need to define what should happen when
// the user selects a row in the sales order (master) table
salesOrders.attachRowSelect(function (event) {
    var Filter = sap.ui.model.Filter,
        FilterOperator = sap.ui.model.FilterOperator,
        selectedRowContext = event.getParameter("rowContext"),
        selectedSalesOrderID = salesOrderModel.getProperty("SalesOrderID", selectedRowContext),
        selectedSalesOrderLineItems = selectedRowContext + "/salesorderlineitems";
    // load the line items for the selected sales order
    lineItems.bindRows(selectedSalesOrderLineItems);
    // create a filter based on sales order ID
    var filter = new Filter("SalesOrderID", FilterOperator.EQ, 'SO:' + selectedSalesOrderID);
    // load the issues table using the filter
    issues.bindRows(issueCollection, null, [filter]);
});

Now, fire up your Chrome browser with the “disable-web-security” command line. This will suppress the Same Origin Policy which normally doesn’t allow you to do XHR request across domains:

SNAGHTML253f39d

Now you can view index.html in Chrome:

SNAGHTML24dc1e3

We have just build our first mash-up of SAP ERP and SharePoint data using SAPUI5!

Themes

SAPUI5 provides a number of themes. The theme used in this example is called ‘sap_platinum’. You can find other themes in the /resource/sap/ui/*/themes folders. Here are screenshots of the same page using the other themes (click the images to enlarge):

imageTheme: sap_goldreflection imageTheme: sap_ux
imageTheme: sap_hcb imageTheme: base

My first impressions on SAPUI5:

  • Offers a comprehensive set of UI widgets and layouts
  • Huge code base. Definitely not a micro-framework Smile.
  • Amazingly little code is required to get a demo app with OData sources described above up and running.
  • Includes jQuery (version 1.4.4. included in sap-core-ui.js)
  • Includes datajs.js(OData library by Microsoft)
  • Style and idiom is a bit verbose and seems to be influenced by Java. This may be a pro or a con, depending on where you are coming from.

Overall SAPUI5 is a very interesting entrant in the marketplace and sure to further boost the position as HTML5 as UI platform.

I hope this post is helpful in starting to explore the SAPUI5 Beta yourself.

Thanks for reading!

UPDATE (Oct 2012): SAPUI5 now includes mobile controls. See comment below from Ruben.

Advertisements

Building a SAP mobile app with Sencha Touch 2

In this tutorial we are going to build a mobile app which connects to SAP systems through SAP Gateway. The app is developed completely in JavaScript using the Sencha Touch 2 framework. Once development is completed we will package the application as a native iOS app using the new Sencha Packager.

In this tutorial we will cover:

  • an outline of the tools we are going to use
  • JavaScript code for basic functional app
  • running the app in a desktop browser
  • packaging and running the app in the iPhone simulator
  • running the app on your iPhone

Introducing the cast…

Sencha Touch is a leading mobile JavaScript framework. It provides an extensive set of UI components which you can use to quickly build mobile apps that can run on many mobile devices. Sencha Touch 2 (ST2) is the latest version, and currently in Beta 3. Download the latest version.

SAP NetWeaver Gateway is a SAP server add-on that allows mobile devices (and other consumers) to connect to SAP systems using OData-based REST services. The SAP Gateway front-ends SAP systems like ERP, CRM, SRM and enables controlled read/write access to business data using HTTP(S) and OData.SAP Gateway is a centerpiece of SAP’s mobile strategy and many mobile apps provided by SAP and its business partners are using SAP Gateway as well.

At this moment, SAP Gateway (version 2 SP3) only supports OData’s AtomPub XML format.

SAP graciously provides an online demo system where you can test-drive the SAP Gateway and the ERP and CRM systems. Our mobile app is going to connect to this demo system, using the demo user account provided by SAP. On SDN (SAP Developer Network) you will find more information on SAP Gateway and you will also find examples of other integration scenarios.

Same Origin Policy

Our Sencha Touch app will need to contact a remote SAP Gateway server to exchange XML data. In a web application, the browser will enforce the Same Origin Policy (SOP). This implies that the HTML file which contains the application must be served from the same domain, port and protocol as the XHR requests are sent to.

Let’s quickly explore the options:

  • JSONP is not an option: not supported by SAP Gateway. In addition limited to read-only scenarios.
  • Use a reverse proxy, which front-ends both the SAP Gateway and the webserver which serves up the application HTML/JS/CSS files. This way the HTML file and SAP Gateway appear to be on the same server. Good option, but adds a moving part to the solution.
  • Native packaging. In a native app, the SOP does not apply. Say again?  Because the HTML file is called by webkit using the file:// protocol, the SOP does not apply. This means there are no cross domain restrictions.

For our demo app we will opt for native packaging.

A closer look at the SAP data

Before we can start building the app we need to understand the content and structure of the data we will fetch from the server. Our first basic app will fetch Activity data from the SAP CRM demo system. Let’s lookup the activities in the SAP CRM system, so we know what activity data to expect:

image

As you can see, there are 5 open Activities for the demo user. To retrieve the activities from the SAP Gateway as an OData feed, get the following service url:

http://gw.esworkplace.sap.com/sap/opu/sdata/IWCNT/ACTIVITY/ActivityCollection?sap-user=<username>&sap-password=<password>

Visit the SAP Gateway Demo page to find the username and password to use.

The server will respond with XML in OData atom feed format. Each activity is contained in an “entry” element.image

The entry element contains the properties of an Activity:image

Developing the SAP mobile app

Enough talk, time to start coding! Our initial goal is to build a first, very basic mobile app which will show the user’s open Activities in SAP CRM. In Sencha Touch we need the following classes:

  • an Activity model: a class representing an Activity (on the client side!).  Defines the fields we want to use.
  • a Gateway Proxy: responsible for fetching data from the SAP Gateway server. It cleverly uses the field definitions of the Activity model to parse the response into Activity model instances which are handed over to the store.
  • an Activities store: holds a collection of activities in the browser. The store is filled by the output of the Gateway Proxy
  • an ActivityList: a UI component which renders a list of activities contained in the store

Looking at the structure of the OData xml, we can define the Activity model class. The fields configuration tells which properties we want to be extracted from the server response into our activity objects. The fetching and parsing is delegated to the proxy:

// Define the Activity model
Ext.define('App.model.Activity', {

    extend: 'Ext.data.Model',

    // the proxy config requires the sap_gateway
    requires: ['App.proxy.sap_gateway'],
    config: {

        // define the fields which we want to extract from the OData response.
        fields: [
            { name: 'Description', type: 'string' },
            { name: 'ActivityLocation', type: 'string' },
            { name: 'CategoryDescription', type: 'string' },
            { name: 'PriorityDescription', type: 'string' }
        ],

        // provide a proxy configuration
        proxy: {
            // use the special sap_gateway proxy
            type: 'sap_gateway',
            url: "https://gw.esworkplace.sap.com/sap/opu/sdata/iwcnt/activity/ActivityCollection?sap-user=<usern>&sap-password=<passsword>"
        }
    }
});

As you can see, the Activity model uses a special proxy type, identified by ‘sap_gateway’. ST2 will look for classes with an alias of proxy.sap_gateway. Also note that you need to require the proxy class in the Model before you can use it.

The proxy encapsulates the communication with remote SAP Gateway server. Our proxy needs to convert the XML data response to Model instances. Because the response is in XML, we can use an XML reader to extract the data from the server response. The XML reader uses the Activity model field definitions to determine which data to extract. With that, the SAP Gateway proxy is remarkably simple:

Ext.define('App.proxy.sap_gateway', {

    // The SAP Gateway proxy extends the built-in Ajax proxy
    extend: 'Ext.data.proxy.Ajax',

    // It requires an XML reader class to parse the response
    requires: 'Ext.data.reader.Xml',

    // Set an alias, so we can use { type : 'sap_gateway' }
    alias: 'proxy.sap_gateway',

    config: {
        // Disable use of paging params (limit, page, start)
        enablePagingParams: false,

        // Disable cache busting
        noCache: false,

        // Configure a XML reader to process the server response.
        // Each activity is contained in an 'entry' element.
        reader: {
            type: 'xml',
            record: 'entry'
        }
    }
});

Please note:

  • this proxy class is re-usable in many Model/Stores combinations. SAP backend systems provide a vast amount of ‘classes’ (in OData parlance: Entity Types): Activities, Accounts, SalesOrders, Currencies, Invoices, etc.You can re-use this proxy with any of these Entity Types.
  • the proxy is read-only: to fetch data from SAP Gateway. A read-write proxy will require additional code.

With the Activity model and Gateway Proxy in place, we can define the Activities store class:

// An Activity store is just a basic Store,
// with a preconfigured Activity model.
Ext.define('App.store.Activities', {
    extend: 'Ext.data.Store',
    config: {
        model: 'App.model.Activity'
    }
});

This completes the data part of the application. Now let’s turn to the UI. We need a List to show the results when the Store has loaded the data from the SAP Gateway.

Ext.define('App.view.ActivityList', {
    extend: 'Ext.List',
    requires: ['Ext.TitleBar'],
    config: {
        // make sure this view uses the full viewport
        fullscreen: true,

        // item template: for each activity in the list,
        // show the description
        itemTpl: '{Description}',

        // put a titlebar on top of the list
        items: [{
            xtype: 'titlebar',
            docked: 'top',
            title: 'SAP CRM Activities'
        }]
    }
});

The most important part is the itemTpl configuration. The item template determines how to format the activities properties in a list item. We’ll keep it very simple and just output the Description field.

Next, we need to tie all the classes together in an Ext.application and tell the application what to do upon launch.


// Don't let the loader generate unique urls to prevent caching
Ext.Loader.setConfig({
    disableCaching: false
});

// Configure the application
Ext.application({
    name: 'App',
    models: ['Activity'],
    views: ['ActivityList'],
    stores: ['Activities'],
    launch: function () {

        // Create an Activity store
        var activityStore = Ext.create('App.store.Activities');

        // Create an ActivityList which takes its
        // data from the Activity store
        Ext.create('App.view.ActivityList', {
            store: activityStore
        });

        // Instruct the store to load the Activities from the
        // server. After the Activities are received, the activity
        // list is updated automatically.
        activityStore.load();
    }
});

As you can see, the App requires the Activity model, the Activities store and the ActivityList. Upon launch, all the application does is create a store, create the view and load data into a store.

To complete our first SAP mobile app, we will need an index.html to contain the ST and app files.

<!DOCTYPE html>
<html>
    <head>
        <title>SAP demo</title>
        <link rel="stylesheet" href="lib/sencha-touch.css" type="text/css">
        <script type="text/javascript" src="lib/sencha-touch-debug.js"></script>
        <script type="text/javascript" src="app/app.js"></script>
    </head>
    <body>
    </body>
</html>

ST2 comes with various builds. I use the sencha-touch-debug.js version, which contains the core library and uses a dynamic script loader to fetch dependencies. I found this worked well in a development setting. However, I disabled cache busting. This prevents the loader from appending unique strings in the url when it fetches the source files. Otherwise you will loose your debugging breakpoints when you refresh your browser.

Running the app in the desktop browser

OK, time to run and test the app in our desktop browser! We’ll use an http server on our local machine to serve the files to our browser. I use the development server built into VS2010, but anything will do. Let’s assume your http server runs on port 35000 and your index.html file is served at http://localhost:35000/index.html .

Remember that we need to overcome the SOP restriction to be able to test the app in our desktop browser? Luckily Chrome provides a command line option which disables the SOP.  (Safari has the same switch but it only takes effect on a Mac, not in Windows).

So, start your Chrome browser with –disable-web-security command line option:

C:\path\to\chrome.exe –disable-web-security

Enter the url for the index.html:

http://localhost:35000/index.html

And lo and behold, we see the Activities that are fetched from the remote SAP CRM system!

SNAGHTML1989016

Let’s look at Network traffic in Chrome Developer tools. You can see all the traffic caused by the dynamic loader. Our call to the SAP Gateway system is indeed allowed by Chrome (try using Chrome without the disable-web-security option and it will throw an error).

SNAGHTML19d1211

Use the XHR option in the bottom toolbar to only show the requests to SAP Gateway.

Extending the app

Now we have this basic app running we can start adding functionality. Next to CRM Activities, the SAP Gateway demo systems offers access to other business data like CRM Accounts and Sales Orders. By adding models, stores, views and navigation we can create a more advanced SAP mobile app. Check out the Sencha Touch 2 guides to see what’s possible. Here are some examples of views and detail screens that you can build:

imageimage

imageimage

Packaging and running the app on iPhone simulator

Now our application development is completed, we are going to package it up as a native app, using the new Sencha packager which is part of ST2. We are going to create a native package for iOS. See this guide for more info.

To use the Sencha Packager you will need a Mac OS X machine with the latest XCode installed. Following the guide, you need to create separate configuration files for each type of deployment. In this tutorial we will create 2 configuration filess:

  • simulator.json: to run the app in the iPhone Simulator
  • development.json: to run the app on your own iPhone

I created the following folders structure to house the demo project. Please note that the www folder contains all the code which we developed earlier. The app folder contains a typical folder layout for a Sencha Touch app.

image

The content of the simulator.json file is:

{
  "applicationName":"SapDemo",
  "applicationId":"com.oxida.sapdemo",
  "outputPath":"~/Projects/SapDemo/simulator",
  "versionString":"1.0",
  "webAppPath":"~/Projects/SapDemo/www/",
  "configuration":"Debug",
  "platform":"iOSSimulator",
  "deviceType":"iPhone",
  "orientations": ["portrait"]
}

This configuration tells the packager to build a version of the app for Simulator and place the SapDemo.app file in the simulator folder.

We have all the files and folders in place. Let’s package and run the app. Open a Terminal window, change to your working directory and run the packager:

image

If all is well, your iPhone simulator will start and run the app:

image

This screencast shows the app in action on the iPhone simulator.

Running the app on an iPhone

Finally, we want to run our app on a real device. To run the app on a real device you need certificates and provisioning profiles, which you can request from the Apple Developers Provisioning Portal (you will need a Apple iOS developer license).

The use of certificates and profiles is,… well …, non-trivial. So if you are new to this, check out some tutorials which explain the concepts in more detail. For those of you who are already familiar with certificates and profiles, here’s a high-level reminder.

You want to… Certificate needed Provisioning profile needed
Run app in iPhone simulator none none
Run app on your own device Development certificate Development
Send app to selected group of users (e..g. beta tester) Distribution certificate Ad Hoc Distribution
Send app to the App Store Distribution certificate App Store Distribution

Now, back to the Sencha Packager!

To create a version of the app that can run on a real device, we need to set the configuration option to “Release”. The Sencha packager will by default use your distribution certificate and App Store distribution profile. For a deployment to your own device, you have to explicitly specify your developer certificate, using the certificateAlias option.

So the development.json  configuration file contains:

{
  "applicationName":"SapDemo",
  "applicationId":"com.oxida.sapdemo",
  "outputPath":"development",
  "versionString":"1.0",
  "webAppPath":"~/Projects/SapDemo/www/",
  "configuration":"Release",
  "platform":"iOS",
  "deviceType":"iPhone",
  "certificateAlias":"iPhone Developer: Your Name",
  "orientations": ["portrait"]
}

Please note: at this moment (Feb 16, 2012) this requires you to install a patch described in this forum post. This patch will be included a future update of Sencha SDK tools versions and I will update this tutorial when the patch is not required anymore.

We can now run the Sencha Packager using development.json as configuration file and build the app. This will create an app version which is code-signed with your developer certificate. As a last step, transfer the Development provisioning profile and the app file to your device. You can use this using iTunes, Organizer in xCode or (my preference) the iPhone Configuration Utility.

Now you should be able to start your app from your device. This is a screenshot from my iPhone:

IMG_0783[1]

Phew..! It’s been a long way but we have our SAP mobile app running on an iPhone!

Where to go from here

We gone through the steps to create a basic app and deliver a development version to our iPhone device. As next steps you can:

  • Further extend the app
  • Create a production build from your Javascript sources (concat source files, change the ST lib build)
  • Create an AdHoc distribution and distribute to up to 100 testers

For final deployment you can consider the Apple AppStore, Enterprise deployments or SAP Mobile App Store, which offers and sells mobile apps build by SAP and its partners.

I hope this post has inspired you to further explore the exciting opportunities of using Sencha Touch 2 to create custom mobile experiences for users of SAP systems.

Thanks for reading!

Building SharePoint Web Apps using Sencha Touch

In this post we will explore how to build a SharePoint mobile web app using Sencha Touch, a great mobile JavaScript framework.

SharePoint 2010 is a very comprehensive platform, but mobile support is fairly limited. Although it is technically possible to customize the mobile user interface on the server side, this is not allowed in cloud based SharePoint (e.g. in Office365).

An alternative  approach is to build a web app where the UI is generated in the browser and connects to one of the SharePoint APIs to access data stored in SharePoint. SharePoint 2010 offers three data APIs which can be used in web apps:

  • SP Web Services (SOAP)
  • REST (ListData.svc)
  • CSOM (Client Side Object Model)

Although each of the APIs offer a distinct set of capabilities, the REST interface is simple and lightweight and I prefer this for mobile use. The SharePoint REST interface offers full CRUD access to items in SharePoint lists and libraries, which might be all that you need for your mobile web app.

Getting started

Because of the Same Origin Policy, your html file must be served from the same domain as the SharePoint site you want to access. You can place your html file containing your app on the server file system or in a SharePoint document library (e.g. site assets).

If you are using SharePoint Online (Office365), you will notice that when you select a .html file in a doc library, it is presented as a download, not opened in the browser. This is due to SharePoint settings which you are not allowed to change in SharePoint Online. As a workaround, simply use an .aspx file extension, instead of .html. This way, you can start your single page SharePoint application from a file in an asset library.

So, to get going, you need to create a app.aspx file to include all the css and js for your Sencha Touch app.

<!DOCTYPE html>
<html>
    <head>
    <title>SharePoint web app example</title>
       <link rel="stylesheet" href="sencha-touch.css" type="text/css" />
       <link rel="stylesheet" href="app.css" type="text/css" />
       <script src="sencha-touch.js"></script>
       <script src="app.js"></script>
    </head>
    <body>
    </body>
</html>

The app.js file contains the basic Sencha Touch start-up and shows an alert:

new Ext.Application({
  launch: function () {
    Ext.Msg.alert('Hello world', 'Ready for action!')
  }
});

You can put these files  in any SP doc library. Let’s assume you have put this file in the Site Assets library of a SP site called demo:

image

Now you can open up the following url on your iPhone or Android device:

http://<SPserver>/demo/siteassets/app.aspx

After logon, you will see the following result:

IMG_0724

Not quite what we expected… SharePoint has detected that we access the page with a mobile device and responds with the default mobile UI. You can suppress the default mobile UI by appending ?mobile=0 to the url.

So, let’s try: http://<SPserver>/demo/siteassets/app.aspx?mobile=0

IMG_0725

Yes! We now have a Sencha Touch web app running of a SharePoint server.

OData proxy

The next step is to connect Sencha Touch models and stores to SharePoint items and lists through the REST interface using the OData protocol. For this you will need an OData proxy. I developed an OData proxy as a Ext user extension. It is designed to access SharePoint data using the SharePoint ListData.svc REST service which is based on OData. You may use it for other OData sources.

The OData SharePoint proxy is on GitHub: https://github.com/lstak/SharePoint-proxy-for-Sencha-Touch

Ext.ux.ODataProxy features:

  • create, read, update and delete SharePoint items as Sencha Touch models
  • fetch multiple items from a SharePoint list in a Sencha Touch store
  • JSON payloads
  • partial updates: only changed attributes are sent to the server during an update
  • fixes issues in Sencha Touch data Model implementation (e.g. missing destroy() method)

Let’s look at some examples how you can use the SharePoint proxy. In these examples we will assume you have a subsite ‘/teamsite’ in which you have created a Contacts list based on the standard Contacts list template.

First we need to define the Model.

var Contact = Ext.regModel('Contact', {
  fields: [
      // please note CamelCase convention for SharePoint column names
      { name: 'Id', type: 'int' },
      'LastName',
      'FirstName'
    ],

  idProperty: 'Id',

  proxy: {
    // use the special odata proxy defined in odataproxy.js
    type: 'odata',

    // the proxy will connect to the List
    // named 'Contacts' in the /teamsite subsite
    url: '/teamsite/_vti_bin/ListData.svc/Contacts'
  }
});

We can now use the following CRUD operations on the Contact data model:

// Create an instance
var contact = new Contact({ LastName: 'Johnson', FirstName: 'Al' })
contact.save();
...

// Read an instance from the server by id
var id = 200;
Contact.load(id);
...

// Update an instance, loaded from the server
Contact.load(id, {
    success: function (contact) {
        contact.set("LastName", "Maxwell");
        contact.save();
    }
});
...

// Delete an instance
Contact.load(id, {
    success: function (contact) {
        contact.destroy()
    }
});
...

Using the Contact model you can now easily define a Store to fetch multiple items:

var store = new Ext.data.Store({
    model: 'Contact'
});

store.load()

Build your application

Using the odata proxy to configure your Models and Stores that connect to the SharePoint server, you can develop your app further just like any other Sencha Touch app. Check out the tutorials on the Sencha site for more info.

I will conclude with an example of a contact list through the normal SharePoint UI and through a Sencha Touch app:

image

IMG_0743[1]

Just as easily, you can expose the content of document libraries:

image

IMG_0744[1]

Live demo

A live demo is available at http://oxida.sharepoint.com/demo. You can open the mobile web app and also explore the content of the SharePoint site with a desktop browser.

image

Sencha Touch and SharePoint are a great combination and open up exciting new opportunities for companies using SharePoint. Give it a try!

Working with SharePoint data in Backbone.js

In this post we will explore how to work with SharePoint data in a browser based application using the popular Backbone.js library.

SharePoint and Backbone.js

SharePoint 2010 offers 3 APIs which can be used in JavaScript apps:

  • SP Web Services
  • SP Client Side Object Model (CSOM)
  • SP ListData service (ListData.svc)

While Web Services and CSOM provide an wider range of capabilities, ListData is a lightweight RESTful interface offering CRUD access to SharePoint lists and items. It is based on the OData specification.

Backbone.js provides models and collections to work with data on server. Out of the box, models and collections can connect with a RESTful JSON API following common REST conventions. However, to work with the SharePoint ListData interface, some customization is required.

Enter…

Backbone.SharePoint

Backbone.SharePoint is a Backbone.js plugin providing special Models and Collections which you can use to work with SharePoint Items and Lists. You can find Backbone.SharePoint on GitHub.

Backbone.SharePoint allows you to:

  • create, read, update and delete SharePoint items as Backbone models
  • fetch multiple SharePoint items from a list as a Backbone collection
  • support for OData query string options ($orderby, $filter, $select, etc.)
  • custom sync() to communicate with the ListData service
  • partial updates: only changed attributes are sent to the server during an update.

Getting started

Because of the Same Origin Policy, your html file must served from the same domain as the SharePoint site you want to access. You can place your html file containing your app on the server file system or in an asset library.

<!doctype html>
<html>
   ....
<script src="jquery.js"></script>
<!-- you can also use zepto.js -->

<script src="underscore.js"></script>
<script src="backbone.js"></script>
<script src="backbone-sharepoint.js"></script>
  ...

</html>

Examples

Now let’s look at some examples how you can use Backbone.SharePoint. Let’s assume you have a subsite ‘/teamsite’ in which you have created a Contacts list based on the standard contacts list.

// You define a Contact Model for items by extending Backbone.SP.Item
// Required parameters are the SharePoint site and the name of the list

var Contact = Backbone.SP.Item.extend({
    site: '/teamsite',
    list: 'Contacts'
})

// Create a new contact, the attributes refer to item column names.
// Please note capitals. We follow SharePoint columnnames
var contact = new Contact({ LastName: "Davis" });

// At this point we have a new contact model, but is not saved to the server,
// so let's save it to the server.
contact.save();

  ....

// Update the attributes of the Item:
contact.set({FirstName: "John"});
contact.save(); 

  ...

// Finally, to remove an item:
contact.destroy();

Working with SharePoint lists is similar to collections.

// you can define a SP List by refering to the model
var Contacts = Backbone.SP.List.extend({
    model: Contact
})

// create a list
var contacts = new Contacts;

// get contacts list from the server
contacts.fetch()

// the fetch options allow you to use query options
// for example, the request below will fetch
// only the LastName and FirstName columns
// for item 11..15 when ordered descending by LastName
contacts.fetch({
    select: 'LastName, FirstName',
    orderby: 'LastName desc',
    top: 5,
    skip:10
})

   ....

// This is how you can create a new contact,
// save it to the server and add it to the list (collection)
contacts.create({
    LastName: "Peel",
    FirstName: "Emma"
})

Hopefully this is sufficient to get you going!

Welcome

Welcome to … And All That JS, a blog about JavaScript application development. I plan to write about JavaScript techniques and list useful resources on the internet.

The blog’s title refers to the expression “… and all that jazz”, meaning “and everything else” which kind of fits the combined use of JavaScript, CCS and HTML in web app development.

 

I hope you enjoy and look forward to your comments!