Friday, 12 May 2017

Calling an Azure Function from a SharePoint Framework web part (SPFx) – part 2: calling from SPFx and passing data

In the previous post we looked at the work to get started with an Azure Function which can be called from an SPFx web part, and can use the PnP core library. Having got our Function set up with CORS, NuGet support for PnP, and deployment from source control we’re now ready to finalise the code. As a recap, I’m splitting the steps and code into these two articles:

Part 1 – setup, use of PnP Core, CORS and continuous deployment (previous article):
  • Initial creation of Function
  • Deployment of function code (using Continuous Deployment from GitHub)
  • Allowing CORS on an Azure Function so that it can be called from JavaScript (including a special entry for the SPFx workbench)
  • Use of SharePoint PnP Core library - via Azure Functions NuGet support
  • Use of App Settings (environment variables) in an Azure Function
Part 2 – calling from SPFx and passing data (this article):
  • Code for the Azure Function side (i.e. the work to create a modern page from some passed parameters)
  • Code for the SPFx side
  • Passing data to the function and adding correct headers
  • Returning data from the function, and displaying success/failure in our SPFx web part

The code – Azure Function side

I’ll talk about a couple of notable things first, and then show the full code.

Collecting data passed from client

We’re going to assume the caller is going to POST data to the function as JSON in the request body – the boilerplate function code does this too, so we’re just extending things a little. We can then access the pieces of data through the Content.ReadAsync() method using a C# dynamic variable and plain object type:

// collect site/page details from request body.. 
  dynamic data = await req.Content.ReadAsAsync<object>(); 
  string siteUrl = data.SiteUrl; 
  string pageName = data.PageName; 
  string pageText = data.PageText; 

  log.Info($"Received siteUrl={siteUrl}, pageName={pageName}, pageText={pageText}"); 

If you wanted to be “tighter” on how data is passed to the server, you could implement a specific class with appropriate fields/properties of course, and specify that as the type that .NET’s ReadAsAsync method should attempt to deserialize the request body to (instead of ‘object’). But, the code above works as a simple way to collect some data passed to the Function – on the client side, of course, we would ensure an appropriate string of JSON with the same properties is set to the body of the request (shown below).

Returning in the right way to the client

We want our endpoint to respect the HTTP conventions here by returning a HTTP 200 if all is good, some appropriate 4xx error code if the request isn’t valid, and so on. We can use the HttpRequestMessage.CreateResponse() method to do this. One example where we might want our Function to send back an error is when it’s called from the SharePoint Framework local workbench instead of a real site – if we don’t have a site, how can our Function know where to create the page? So, I send back a HTTP 400 (bad request) in this case:

if (siteUrl.Contains("www.contoso.com")) { 
   // N.B. the “www.contoso.com” URL indicates the local workbench in SPFx.. 
  return req.CreateResponse(HttpStatusCode.BadRequest, "Error: please run in the context of a real SharePoint site, not the local workbench. We need this to know which site to create the page in!"); 
} 

..and now this will show up in browser dev tools/Fiddler/other monitoring tools in the right way and so on.

The full code for the Azure Function to create a modern page is:

We’ll get to the SPFx web part soon, but note that the Azure portal gives you a nice way to test your Function before you’ve actually created a caller. You can craft the request body so that it contains the JSON with the properties the function expects, and then press the “Run” button to see the results. Your Function will execute, and any logging messages you have will be output to the console:

SNAGHTMLb00f684_thumb2

Integrating the function with an SPFx web part

So, let’s create a simple SPFx web part which calls our Function. We’ll provide a simple form to collect parameters for the page name and default content, and a button to actually call the Function and get the page created. I didn’t do anything fancy here – I ended up with this:

SNAGHTML1551102d_thumb2

So let’s head towards that. First, we need the Function’s URL – we can get this from the Azure portal. Find this link to do that:

SNAGHTMLad22f4c_thumb2

Now we need to think about calling our Function from SPFx. You can do this using the HttpClient object of course, but there are some things to know to structure the request correctly in terms of the headers and body content. We also need to collect some information from our form fields, but the important thing when POSTing data in SPFx is the use of the Headers and IHttpClientOptions objects in the SharePoint Framework:

const requestHeaders: Headers = new Headers();
requestHeaders.append("Content-type", "application/json");
requestHeaders.append("Cache-Control", "no-cache");

let siteUrl: string = this.context.pageContext.web.absoluteUrl;
let pageName: string = (document.getElementById("txtPageName")).value;
let pageText: string = (document.getElementById("txtPageText")).value;

console.log(`SiteUrl: '${siteUrl}', PageName: '${pageName}.aspx', PageText: '${pageText}'`);

const postOptions: IHttpClientOptions = {
   headers: requestHeaders,
   body: `{ SiteUrl: '${siteUrl}', PageName: '${pageName}.aspx', PageText: '${pageText}' }`
};

..and then we can use this set of headers/body content (in the ‘postOptions’ object) in a POST request to our Function with the httpClient.post() method:

this.context.httpClient.post(this.functionUrl, HttpClient.configurations.v1, postOptions).then((response: HttpClientResponse) => {
    // code omitted..
}

The important thing here is to trap any non-success responses from the server, by ensuring your call to httpClient.post() has both an 'onFulfilled' and 'onRejected' handler (the first and second parameter respectively). You can see this in the full code for the SPFx web part below:

So, with the code for both the server and client side sorted, our web part can now create a modern page using the PnP methods. Because the Azure Function sends back the appropriate responses, it’s easy for us to display success/failure in the UI.

Success:

SNAGHTML15522249_thumb2

Failure:

SNAGHTML1553a261_thumb2

Assuming all was well, a new modern page is created in the current site as you’d expect:

SNAGHTML155478cc_thumb2

Note that if you want to recreate this web part, you'd also need to some styles in your *.module.scss file:

Summary

In addition to “timer” processes and processing of Azure BLOBs and Queue items, Azure Functions are a great way to add little bits of .NET code to your client-side apps and web parts. We showed it in the context of a SharePoint Framework web part here, but any kind of JavaScript could call our Function – that includes mobile apps, SharePoint Content Editor web parts, SPFx extensions/customizers (the replacement for JSLink/CustomActions etc.) and so on. We went through enabling CORS, bringing in the PnP Core component through the project.json file for NuGet, setting up source control integration and other steps.

Other things you might need to think of in production include authentication. My example used “Function auth + SharePointOnlineCredentials”, but you might want to secure your Function with AAD instead and use true app authentication within your code. That would mean first registering an AAD app and obtaining an access token through adal.js, which is certainly more complex and presents an issue around Reply URLs if your web part can be on multiple pages. Vesa and Waldek talked about an interesting alternative which calls the Function through an IFrame (with cookies), but that only works if your code can use app-only auth. It would be nice to have better options than this in the future to be honest.

Consider also that Functions are evolving beyond .csx files, so although you could always reference your own DLLs from there and factor your code that way, other more direct ways of using class libraries now exist too – see Publishing a .NET class library as a Function App

Either way, Functions are a great tool and as a developer you’ll benefit from being familiar with them :)

Wednesday, 26 April 2017

Calling an Azure Function from a SharePoint Framework web part (SPFx) – part 1: use of PnP Core, CORS and continuous deployment

Azure Functions are the cool thing these days, mainly because they’re just so damn useful. They can work out cheaper than other hosted code (e.g. WebJobs), but they’re also very flexible. You can define a function which runs on a timer or when an item is added to a queue (just like a WebJob), or you can define one to execute when when a HTTP endpoint is hit. In that respect it’s similar to a web API, but simpler and with less of the infrastructure. If I have a button in a page or web part that needs to do something that’s simpler on the server-side (use a method in the OfficeDev PnP library perhaps), I really like the idea of hooking the button up to an Azure Function. My click handler would then POST to the Function’s endpoint, the server-side code will execute, and then the client-side code will receive the response.

I thought it would be good to show this in a SPFx web part – and to show how data is passed between client and server. We’ll use the example of having our Azure function make use of the OfficeDev PnP core library. Specifically, my web part provisions a modern page into a site and adds an SPFx web part – both things that are harder to do purely on the client side. I’m splitting this into two articles, but overall I’m trying to show a few things here:

Part 1 – setup, use of PnP Core, CORS and continuous deployment (this article):
  • Initial creation of Function
  • Deployment of function code (using Continuous Deployment from GitHub)
  • Allowing CORS on an Azure Function so that it can be called from JavaScript (including a special entry for the SPFx workbench)
  • Use of SharePoint PnP Core library - via Azure Functions NuGet support
  • Use of App Settings (environment variables) in an Azure Function
Part 2 – calling from SPFx and passing data (next article):
  • Code for the Azure Function side (i.e. the work to create a modern page from some passed parameters)
  • Code for the SPFx side
  • Passing data to the function and adding correct headers
  • Returning data from the function, and displaying success/failure in our SPFx web part

Overall we’ll have a solution which creates a modern SharePoint page from a button click – but the pattern can be used for whatever you want.

Azure Functions – different types, and use of .NET code

As a reminder, when you define a function you specify the trigger – these include:

  • HTTPTrigger (the one we’ll use here)
  • TimerTrigger – execute something on a scheduled basis, as you could do with a WebJob
  • QueueTrigger – execute when something is added to a queue
  • Lots of others – including BlobTrigger and “Generic webhooks” (N.B. not currently what you’d use for SharePoint webhooks)

Of course, use of a HTTPTrigger doesn’t need to be in response to a button click – it could be any time you can post something to the server-side. But a button-click is what we’ll use here. Picking up some awareness of pricing of Azure Functions is a good idea, in particular the difference between the Consumption plan and an App Service plan. In most cases the Consumption plan is what you’ll prefer (and your costs will be peanuts), except perhaps for production implementations that need to run at scale and/or when you have existing resources running on an App Service plan. Anyway, that’s a slightly different topic and you can read more at Choose the correct service plan for Azure Functions.

Developing Azure Functions code with .NET
 
If you're new to Azure Functions, note that the development model for .NET is different in a couple of places:
  • There's no Visual Studio solution - it's just a collection of files in a directory
  • The main file is a csx file, and you won't get IntelliSense support here
  • You can deploy other assemblies and make use of NuGet packages, but this is done in a different way
Overall, I find a nice approach for developing more complex things is to use Visual Studio to create a DLL/library project for most of my logic, with an accompanying console app to help with local testing. The DLL gets deployed to the BIN directory of the Azure Function, and you can call into it from the function code (CSX file). That’s not the only way, but I like it. However, in the example shown here we’ll keep things simple by having our implementation in the .csx file.

Let’s get started on creating our function.

Create the Azure Function app

  1. Open the Azure portal, and find the option to create a new Function App.
  2. Specify the app name and any existing Resource Group and Storage Account that you may have (if not, you’ll need to create these prerequisite containers):

    SNAGHTMLf8d5e49
  3. Once the app has finished creating, find it in the portal (e.g. by accessing the “recent” section) and click into it.
  4. Click the “New Function” button: 

    SNAGHTMLc1a9b2
  5. I then prefer to use the “custom function” path:

    SNAGHTMLc42e0b
  6. Find the “HttpTrigger-CSharp” template (you might want to filter down to C# templates only using the dropdown):

    SNAGHTMLc603f4
  7. Give the function a name – we’ll also leave the “Authorization level” as the default of “Function”: 

    SNAGHTMLc6a17c
  8. Click Create – you should now see the integrated editor: 

    SNAGHTMLc79300
  9. You can now play around with running the function – consider how data is passed to the function and returned for example.

You’ll find that the integrated editor is great for some initial playing. But as you get more serious, I personally find it slicker to set-up some continuous deployment from source control, whether that’s GitHub or Visual Studio Online. Yes, you do have to commit/check-in each time you want to test your updated code – but that feels better than a) using the integrated editor and b) manually copying files using the Kudu interface. If source control integration feels a bit heavyweight for your needs, consider also the options of sync’ing from OneDrive or Dropbox – cool stuff I suppose, at least if you don’t really need true source control.

Deploying files to an Azure Function via GitHub integration (optional)

So this is an optional step, but since we’re here let’s go through the high level. The only complexity is doing things in the right order and making sure your folder is “at the right level” for how an Azure Function expects to have it’s files. The process isn’t hugely different for other systems such as Visual Studio Online by the way. The full documentation is at https://docs.microsoft.com/en-us/azure/azure-functions/functions-continuous-deployment, but here’s my summary with screenshots:

  1. First create yourself a repository:

    SNAGHTML128454d7
  2. Then clone it to the directory you want to work in on your machine:

    SNAGHTML554aec3

    SNAGHTML555fdb9
  3. Now open the directory in VS Code, and add the files for your function – you need to obtain these by copying from the Function you created earlier. It’s only a a couple of files, so I just copy the contents between the Azure portal and a file I created in VS Code. At this point it’s just the default content of these files – so I suggest making a simple code change to help us check later that the deployment is happening properly (e.g. make a simple change to one of the logging statements):

    SNAGHTML55e7b7e
  4. Once your files are ready, go back into the Azure portal and find the “Deployment options” link where continuous deployment is configured:

    SNAGHTML55fbe5e

  5. Click that link, then click the “Setup” button and choose GitHub:

    SNAGHTML56094e8
    SNAGHTML5615bd2
  6. Now walk through the process of pointing to your GitHub repository and providing authentication details:

    SNAGHTML56233f2
  7. Now we need to commit our files to GitHub – you can do this from the command-line if you like, or using VS Code like this:

    SNAGHTML5637423
    SNAGHTML5656a84
  8. Once the code is checked-in, examining the file in the Azure Functions portal should show your code change:

    SNAGHTMLba69a24

So, we’re now in good shape for developing our code. We’re no longer restricted to the web-based editor – instead, we can use whatever editor we like and properly store our Azure Function files in source control.

Enable CORS on the Azure Function

The next step is to ensure our Function can be called from JavaScript in a web page – and that means CORS support must be added. This is critical even to be able to test with the SPFx workbench, and we need to specify whatever URLs we will use (including the local workbench URL). To do this, find the “CORS” setting in the “Platform features” area of your Function:

SNAGHTML7350bc9

Once in there, specify any URL domain which will host JS code which will call into your Function – as in the image below, this is typically your Office 365 tenant domain(s) and also the local SPFx workbench URL (https://localhost:4321):

SNAGHTML73c8aab

Click “Save” to store the settings. Your Azure Function can now be called from SPFx.

Add the SharePoint PnP Core component via NuGet

To call into SharePoint PnP methods, the best option is to use Azure Functions support for NuGet – as an alternative you could obtain the PnP DLL and upload it to your function’s bin directory, but the NuGet way is more straightforward. The first time the code runs, any NuGet packages you specify as dependencies get downloaded and installed to your Function’s directory.

This is done by adding a project.json file in the directory of your Azure Function:

SNAGHTML7417101

Add content like this to specify the “SharePointPnPCoreOnline” NuGet package:

If you configured source control integration earlier, remember you’ll need to check-in now so that the code builds:

SNAGHTMLad0d181

Configure App Settings for SharePoint credentials

As the final preparation step, we’ll add some App Settings variables to store the username/password used to call into SharePoint (to create our modern page). We’re doing this because we’ll authenticate to SharePoint using SharePointOnlineCredentials (i.e. a simple username and password). For real-life uses you should consider using app authentication, be it of the SharePoint Add-in variety OR the Office 365/Azure AD app variety – in the latter case, you’d need to use adal.js or similar to obtain an access token to pass to your function of course. In this case, we’ll do two things to add some security and manageability to things:

  • Ensure our Azure Function requires simple auth in the form of an “API key” – the calling code must provide this
  • Store the credentials to call into SharePoint as App Settings of our Azure Function – that way they’re not hard-coded anywhere or stored in any particular file in clear text

Configuring App Settings for a Function is just like doing it for any Azure web app:

  1. Go to the “Application Settings” area within “Platform Features” in your Function app:

    SNAGHTMLaebf4f4
  2. Once there, enter a key/value for the username and password you wish to connect to SharePoint with:

    SNAGHTMLaed6414
  3. Click “Save”. These values will now be available to your code.

The corresponding C# code to use in your Function to fetch these values is this:

string ADMIN_USER_CONFIG_KEY = "SharePointAdminUser";
string ADMIN_PASSWORD_CONFIG_KEY = "SharePointAdminPassword";
string adminUserName = System.Environment.GetEnvironmentVariable(ADMIN_USER_CONFIG_KEY, EnvironmentVariableTarget.Process);
string adminPassword = System.Environment.GetEnvironmentVariable(ADMIN_PASSWORD_CONFIG_KEY, EnvironmentVariableTarget.Process);

 

Summary

We’re now complete with the setup steps. Our Azure Function is configured with CORS, is ready to call into the SharePoint PnP Core library, has some App Settings for credentials, and is configured with Continuous Deployment from GitHub. In the next post we discuss the code used on both sides – the TypeScript/SPFx code to call the function, and the code inside the function which does the work - see Part 2 - calling from SPFx and passing data.

Tuesday, 11 April 2017

Speaking at SUGUK London on SPFx dev – April 20, 2017

Next week the UK SharePoint user group (SUGUK) is holding a developer evening at Microsoft Paddington. I’m looking forward to taking part, and will be giving my “Pitfalls in SharePoint Framework (SPFx) development” talk. It will be nice to have a developer focus to the event – there’s a lot of change in the dev landscape at the moment, between Azure Functions, Teams extensibility, modern sites and pages, to the overall shift that the SharePoint Framework brings with it’s emphasis on npm, gulp, TypeScript and so on. My esteemed fellow northerner Bill Ayers is giving the other session – he will open with “Welcome to the brave new world of SharePoint and Office 365 development”. This should be a good intro to the overall topic of the SharePoint Framework, which will lead into my deeper-dive into real world pitfalls and how to avoid them.

The details for the evening are:

SUGUK developer evening

When: 20/04/2017 from 6:30 pm - 9:00pm
Where: Microsoft, Paddington (https://www.microsoft.com/en-gb/about/ukoffices/london-paddington/)
Agenda:

  • 6:00pm – Welcome/registration etc.
  • 6:30pm – Bill Ayers : “Welcome to the brave new world of SharePoint and Office 365 development”
  • 7:30pm – break
  • 8:00pm – Chris O’Brien : “Pitfalls in SharePoint Framework (SPFx) development”
  • 9:00pm – finish/SharePint

You can sign up here – http://uk.communities.tech/events/sharepoint-user-group-london-developer-night/

Topics I’ll be covering include npm, dependencies, re-use of existing JS code using modules, SPFx security, bundling and more.

image

Unfortunately the event doesn’t have recording or streaming facilities, but if you’re in the area it would be great to see you!

Wednesday, 5 April 2017

An intro to Power BI for the Office 365 developer – slide deck

I’ve been working with Power BI a bit recently, and it’s a really interesting world when you come at it from a dev perspective. You can accomplish things quickly that would take a LOT of code, and it’s pretty impressive that way. For a long time I felt somewhat guilty that I had a skills gap with Power BI (“call yourself an Office 365 architect, you can’t even configure a slicer!”), but happily I’ve now gained some knowledge and now sleep better on that front ;) I think it’s easy to overlook Power BI and assume that you need to do custom dev work to meet some client requirements around presenting data – but often it’s possible to flip things around and say “what if we gave you something like this, and it would have benefits X, Y and Z?”

The key highlights for me are:

  • A decent grid control, with sorting/filtering, mobile support etc. – with no code needed to fetch data and bind to the grid
  • Easy charts – and lots to choose from
  • Easy maps
  • Great mobile support
  • Can embed within a web page (e.g. via the SPFx Power BI web part)

To work out whether Power BI is a good fit for your requirements, I summarise it with this slide:

image

The lessons you learn – visuals, filters, slicers…and publishing to a SharePoint page

I felt like I learnt a few lessons as I got started. I even managed to burn 2 or 3 hours fiddling around with the wrong bits after downloading the wrong thing (hint – it’s Power BI desktop you need, not the app in the Windows store!) It also took me a while to get to grips with the basic process of adding visualizations to the report, and getting the controls to work with each other (e.g. selecting an item in a pie chart to filter rows from my data). I tried to capture some of these learnings for a “lunch and learn” chat with my team, but hopefully the slide deck might be useful to others starting out with Power BI.

It’s also particularly interesting now that it’s so easy to embed a Power BI report to a SharePoint page. In case you didn’t know, Microsoft have a new web part which supports this, although it can only be used on modern pages:

image

Just remember that all users need to be licensed appropriately to view the report (i.e. they need an E5 license or a Power BI Pro add-on license, as things currently stand). If that doesn’t work for you, there’s always the “publish to web” option if you’re OK with an anonymous access “everyone can see my data” approach:

image 

The presentation

Anyway, here’s the slide deck in case it’s of use:

 
Happy data presenting!

Tuesday, 14 March 2017

Provisioning modern pages and SPFx web parts

So the SharePoint Framework (SPFx) is now available in all Office 365 tenants, and using SPFx for web parts you’re developing now is a sensible option. After all, we now have “modern” SharePoint sites and pages to consider, and the matrix of what can be used where looks like this:

  • SPFx web part – classic pages AND modern pages
  • Classic web part – classic pages only

That alone should tell you that most new development should be done using SPFx. As a reminder, any SharePoint site that is attached to an Office 365 Group is a modern site, and any old SharePoint Online site that has existed for a long time now has modern pages (it’s the default now when a new page is created). Some time in 2017, I think we can also expect a new framework for publishing sites, and so the overall message is that any web parts you develop with “classic” approaches will have fewer and fewer places they can be used.

However, SPFx initially had a few things missing on the provisioning side:

  1. Ability to add a custom SPFx web part to a page programmatically (or in some other provisioning sense e.g. PnP provisioning XML)
  2. Ability to add one of Microsoft’s out-of-the-box SPFx web part to a page programmatically (or with PnP provisioning XML)
  3. Ability to automatically add an app to a site, so that any SPFx web parts (or other artifacts) are available
  4. Ability to create modern pages programmatically
  5. Ability to create modern site collections programmatically

So, lots of site templating scenarios had gaps in the new world.

The good news is that Microsoft have taken the first steps in closing these. Effectively, items 1, 2, 4 and 5 are now possible. Yes, there are still things that may get in the way of meeting client requirements for SharePoint team sites, but things are progressing and hopefully these will be closed soon.

Specifically, the bad news is that as far as I can see, the lack of item number 3 in the list above means it’s still not possible to provide a template for a SharePoint site which has a home page with certain modern web parts. That’s a shame because that’s a common requirement for us – most collaboration solutions we develop involve team sites which are lightly customized, but that would usually include tweaking the home page to have the most appropriate set of web parts. Here’s a summary of what happens:

Provisioning custom SPFx web parts to pages
At the time of writing (March 2017), any *custom* SPFx web parts you build only become available when the app is *installed to the current site*. I was hoping this wouldn't be the case, and my understanding is that "soon" there will be other options for rolling out web parts across a tenant (to avoid the need to install the app). At least, this should be possible for web parts which don't provision other artifacts, such as a supporting list. But for now, you must install the app to the site, once the app itself has been made available by uploading the .sppkg file to the App Catalog. There is currently no API to facilitate app installs, and the traditional approach to tenant-scope deployments does not work.
 
Additionally, PnP does not currently support provisioning SPFx web parts to pages using an XML template. For now, you have to write code.

So, depending on your requirements you might need to do some “post-processing” of sites once they have been created to get to where you need to be.

Getting started with PnP methods for provisioning modern pages and web parts

All of the code in this article uses Microsoft’s Patterns and Practices methods for provisioning. It’s possible to “code direct” against SharePoint Online, but you’ll find the code is less simple and gives you little/no benefit in doing so – the PnP abstraction here is generally what you want. If you find a scenario where you *do* need more control (one example might be setting “PromotedState=1” to create a news page), then Microsoft’s Customizing "modern" site pages article shows you both code approaches. Effectively in this post I want to supplement that article a bit with some extra info and screenshots, but I recommend being aware of it. In general, you'll need to follow this process to get started:

  1. Get ready with some .NET code (e.g. a console app, Azure Function or something else) - the PnP core library we'll use is based on C# CSOM
  2. Install the https://www.nuget.org/packages/SharePointPnPCoreOnline NuGet package, using a version which is later than 2.13.1703 (March 2017):

    SNAGHTMLd3a2852

When you install the NuGet package you'll also get the right version of the SharePoint CSOM library as a dependency. Once those are installed, you're ready to start coding with the PnP methods. It’s now easy to create a modern site programatically (see https://msdn.microsoft.com/en-us/pnp_articles/modern-experience-customizations-provisioning-sites) or manipulate pages in a site, which is what I’m focusing on here.

Creating a new page vs. modifying an existing page

First things first, note the difference between creating a new page vs. getting a reference to an existing page and modifying that – the code sample below shows both. Also note that a modern page is simply a page set to a particular content type and with some specific properties – it’s this that makes it modern. The PnP methods abstract you from these details, but see the MSDN page at the previous link for the info. With PnP creating or modifying an existing modern page is as easy as:

Provisioning a page with an out-of-the-box modern web part

Thanks to PnP, there’s nothing complex about getting your web part onto the page. A nice method is provided which knows about the out-of-the-box modern web parts –ClientSidePage.InstantiateDefaultWebPart():

That will get you to the point where the web part has been added to the page with the default properties:

SNAGHTML137b5e9a

Incidentally, that method allows you to choose between all of the currently-available out-of-the-box web parts using an Enum:

SNAGHTML137dca4a

Just in case you ever need to know what the underlying web part IDs are, here you go:

Web part ID Web part name
daf0b71c-6de8-4ef7-b511-faae7c388708 ContentRollupWebPart
e377ea37-9047-43b9-8cdb-a761be2f8e09 BingMapWebPart
490d7c76-1824-45b2-9de3-676421c997fa ContentEmbedWebPart
b7dd04e1-19ce-4b24-9132-b60a1c2b910d DocumentEmbedWebPart
d1d91016-032f-456d-98a4-721247c305e8 ImageWebPart
af8be689-990e-492a-81f7-ba3e4cd3ed9c ImageGalleryWebPart
6410b3b6-d440-4663-8744-378976dc041e LinkPreviewWebPart
0ef418ba-5d19-4ade-9db0-b339873291d0 NewsfeedWebPart
a5df8fdf-b508-4b66-98a6-d83bc2597f63 NewsreelWebPart
c70391ea-0b10-4ee9-b2b4-006d3fcad0cd PinnedItemsWebPart
58fcd18b-e1af-4b0a-b23b-422c2c52d5a2 PowerBIReportEmbedWebPart
91a50c94-865f-4f5c-8b4e-e49659e69772 QuickChartWebPart
eb95c819-ab8f-4689-bd03-0c2d65d47b1f SiteActivityWebPart
275c0095-a77e-4f6d-a2a0-6a7626911518 EmbeddedVideoWebPart
31e9537e-f9dc-40a4-8834-0e3b7df418bc YammerEmbedWebPart

 

Setting property values on web parts

Things get a little trickier when it comes to setting properties, but only because discovering the actual property names for each web part currently needs some digging. Here are a couple of examples, using the video embed web part and the Yammer embed web part:

Now I have my web parts properly configured as they land on the page:

SNAGHTML13864eb6

Right now, it’s a little tricky to discover the property names for each web part – but the PnP guys mentioned that in future the SPFx workbench page will help out with this (i.e. add web part to page, then use see the property structure somewhere in the workbench), and that will be nice. For now, I found a couple of options - you can dig around the collection returned from ClientSidePage.AvailableClientSideComponents() in the debugger (more on this method later):

SNAGHTML1389c697

..or an easier way can be to instantiate the web part first using the PnP method, and look at that in the debugger:

SNAGHTML138befa1

Another option is to analyze the “CanvasContent1” page property on your page’s list item (e.g. in the SharePoint Online Client Browser or similar tool). Anyway, once you have the property names you can set them accordingly.

Provisioning a custom SPFx web part

Provisioning a custom web part makes use of a different method in the PnP stack – ClientSidePage.AvailableClientSideComponents(). The process is slightly different in that:

  • The app needs to be installed to the current site, as noted earlier (at least for now)
  • You need to obtain the ID/name of your web part from the manifest, or by analyzing the available web parts collection on the page (once the app has been installed and the web part is indeed available)

Here’s what a method to add a named custom web part to a page might look like – it’s a matter of identifying the item in the collection that represents your custom web part :

The ID and name can be found in the web part’s original manifest file, as shown below:

SNAGHTML1256879f

These values just need to match up with whatever you’re using to identify the web part in your code e.g:

var webPartToAdd = components.Where(wp => wp.ComponentType == 1 && wp.Name == wpName).FirstOrDefault();

SNAGHTML12a2fda8

..anyway, now my custom web part is provisioned to the page (with my truly beautiful color theme in this case!):

SNAGHTML13aa72e8 

Other notes

  • The ClientSidePage.AvailableClientSideComponents() method is an abstraction on the [site]/_api/web/GetClientSideWebParts endpoint. It’s worth taking a look at that.
  • If you look at the full collection returned by these methods, you’ll find there are lots of additional items that I’m not listing in the table above. These are non-web part components such as “SPFilepicker”, “SPHtmlEmbed” and “SPPageContext” – these are other bits of SPFx code which are registered with the framework so that they are allowed to run. I think we can expect that other forms of arbitrary JavaScript we implement will be registered in this way (e.g. JSLink replacement).
  • If you wish, you can add an out-of-the-box web part using ClientSidePage.AvailableClientSideComponents() (since it contains both custom and stock web parts). Just be aware that for out-of-the-box Microsoft web parts the name is always the same as the ID, which is slightly confusing and means you can’t easily reference the part by name in your code. I checked, and this isn’t a PnP bug but is how they come from [site]/_api/web/GetClientSideWebParts:

    SNAGHTML129f4543

    Contrast that screenshot with the one above for a custom web part (where the ID and name are different) - just one to be aware of.

Summary

Adding a modern/SPFx web part to a page is simple with the PnP methods, so long as you're happy to write your own code rather than depend on a PnP provisioning template - but hopefully XML support is coming soon too. Similarly, it’s also easy to create new pages or add to existing pages with those methods. I've run through some scenarios here, but the MSDN article Customizing "modern" site pages has some *great* information, and I highly recommend being aware of it (and the companion articles linked from there). Good job PnP team :) Happy coding!

Monday, 13 February 2017

Web part properties in the SharePoint Framework – part 2

In this post I continue looking at the out-of-the-box controls you can use for web part properties in SPFx. I think the team have done a good job on how web part properties work – in terms of controls, you can always create your own but it’s good to know you often don’t have to. You can represent most things as a web part property - we have dropdowns, checkboxes, sliders, toggles and so on. And as we’ll see here, even the humble button has lots of options.

NOTE – in these articles I provide a description of most important properties of each control (partly based on specific nuances I observe when using them), but be sure to also reference the official docs for those. The main point of my posts is to show the visual screenshot of each control and add some extra tips/info, rather than duplicate the documentation.

Buttons (PropertyPaneButton)

If you need to use a button in the web part property pane, the good news is there are lots to pick from!

What it looks like:

SNAGHTML2e77658e

By the way, those lines between each button are there just because I’m using a horizontal rule (PropertyPaneHorizontalRule control) to separate each one – another useful control you can use to help present your web part properties as you like.

Code:

Buttons all share some common properties which are fairly self-explanatory, but note that some properties only apply to some button types. Here are some key ones, but also see ipropertypanebuttonprops for some extra details on the 'aria' properties for use with screen readers.

Properties:

Property

Description

text Text displayed on the button.
buttonType

Specifies the type of button to show, from PropertyPaneButtonType enum:

  • Normal
  • Primary
  • Hero
  • Compound
  • Command
  • Icon
onClick Pointer to a function to execute when button is clicked. N.B. There are some additional options in terms of interacting with web part properties and context, see the ‘Buttons and bound properties’ and ‘Context – ‘this’ in click handler’ sections for more details.
icon Used for Hero and Icon buttons only. Specify a value that matches up with the name of an icon in the Office UI Fabric icon set - https://dev.office.com/fabric#/styles/icons#icons
description Used for Compund buttons only – displays as the secondary text
disabled Specifies if the control is disabled.

Other aspects of buttons

Buttons and bound properties

Buttons can be bound to properties of your web part – this is optional, and is done by specifying a value for the first parameter which matches up with a property you have defined (notice it’s an empty string for most buttons in my sample above). I see it as a nice way to hook into state/properties for the web part itself. This can be useful if you need to set a web part property when a button is clicked – perhaps you want to track if the button has ever been clicked so that other controls can be enabled/disabled or some other similar scenario. You could update a property ‘manually’ yourself in code, but the nice thing is that the old value is passed to your onClick handler, and returning a value from the function sets the new value, which makes things simple. The pattern is shown below:

private heroButtonClick(oldVal: any): any {
    if (oldVal === "Something old") {
      // do something..
    }
    return "Something new";
  }

In the larger code sample above showing button declarations, you can see that most buttons are not bound to a property (first parameter is an empty string), but the hero button is bound to a web part property named ‘btnHero’. This is defined in my manifest.json file, and like any web part property, I can set a default value there. If you just need a simple button click handler, you can declare the function without a parameter, and without returning a value.

Context – ‘this’ in click handler

If you specify the click handler in the normal way (e.g. onClick: this.cobWPPropButtonClick), then inside the function ‘this’ will represent the button. This is useful if you want to know which button fired the handler, perhaps because you have multiple buttons sharing the same callback. If for any reason you want ‘this’ to represent the overall web part class, you can bind with onClick: this.wpPropButtonClick.bind(this).

Summary:

  • if you want context of button, bind with onClick: this.wpPropButtonClick
  • if you want to stay in context of overall class/web part bind with  onClick: this.wpPropButtonClick.bind(this)
Icons

To use one of the button types which use an icon (Hero or Icon button), you reference a named icon in Office UI Fabric. These can be found at https://dev.office.com/fabric#/styles/icons#icons, and there are lots to pick from:

SNAGHTML353fe5

Indeed, I spent a happy 10 minutes amusing myself by creating buttons with some of the more unusual icons Smile

SNAGHTMLeb041

Haha! Anyway, there’s quite a lot of flexibility there and it’s definitely possible to create buttons which lead to a nice user experience. Just don’t go too crazy Winking smile

Toggle control (PropertyPaneToggle)

The Toggle control is fairly simple, and suits anything boolean-like that can be on or off, enabled or disabled.

What it looks like:
SNAGHTMLd65894d SNAGHTMLd6630a9

Notice that there are separate labels for the on state and the off state, which show appropriately. If you need some code to execute when the toggle changes state, note that your entire getPropertyPaneConfiguration() method runs at this time – in fact, it does whenever a control changes state in a reactive property pane (but not otherwise). That should be a reminder that you need to keep this method lean, and only do async lookups when absolutely needed, not on every execution.

Code:
Properties:

Property

Description

label Text displayed next to the control.
onText

Specifies the text to show when in the ‘on’ state.

offText

Specifies the text to show when in the ‘off’ state.

checked Used to set the state of the toggle.
key A unique key to identify the control.
disabled Specifies if the control is disabled.

Slider control (PropertyPaneSlider)

The slider control is perfect when you want to allow a user to select within a range of values (min/max). It’s quite flexible in that you can choose the ‘step’ value, e.g. to go up in increments of 5, 10, or whatever you need.

What it looks like:
SNAGHTML9c6b7ad
Code:
Properties:

Property

Description

label Text displayed next to the control.
min

Specifies the minimum value (lower bound).

max

Specifies the maximum value (upper bound).

step Specifies the increment that the value can be increased by when the slider is dragged or clicked.

showValue

If the current value should be shown next to the control (enabled in my image).
value Specifies the initial value.
disabled Specifies if the control is disabled.

Choice Group control (PropertyPaneChoiceGroup)

This control allows the user to make a selection, but with radio buttons instead of a dropdown list. It works similar to the DropdownList control in that it takes an array of options. One notable difference here is that if you don’t want plain radio buttons, images can be used too.

What it looks like (radio buttons):
SNAGHTMLeea9bf7
Code (for radio button options shown above):
What it looks like (radio buttons):
SNAGHTML112bb4d7

Note you can actually specify a different image for when the option is selected compared to when it is not.

Code (for images shown above):
Properties:

Property

Description

label Text displayed next to the control.
options The set of IPropertyPaneChoiceGroupOption items to use as the choices.
Properties of the IPropertyPaneChoiceGroupOption:

Property

Description

key Unique key for the item.
text Text for the item.
imageSrc URL of the image to use, when using images rather than radio buttons. (I only tested with absolute URLs so far)
imageSize An object containing height/width properties to specify the size of the image.
checked Used to specify if this item is selected.
disabled Specifies if this item is disabled (not available for selection).

Link control (PropertyPaneLink)

A simple control which allows you to put a link in the web part property pane - could be used to provide a link to some help or guidance for example.

What it looks like:
SNAGHTML1af5543
Code:
Properties:

Property

Description

text Text for the link.
href Hyperlink destination.
target Window to use – options include _self (default), _parent, _top and _blank.
popupWindowProps An object specifying the width, height, title and position of a pop-up window (IPopupWindowProps).
disabled Specifies if the link is disabled.

Summary

There are lots of out-of-the-box controls you can use when implementing web part properties in the SharePoint Framework. In this post we looked at the Button, Slider, Toggle, ChoiceGroup and Link controls – but there are others too, including Textbox, Dropdown and Checkbox controls which I wrote about earlier. A big part of providing a usable web part is often the care taken around how the user will set properties, so knowing what’s in the toolbox is important for SharePoint developers.