Tuesday, 14 March 2017

Creating/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!

4 comments:

Anonymous said...

As always, excellent post, will keep an eye on this as I am sure I will need it in the future developments.

Txe said...

Hi!

Thanks for the great article!

I'm trying to create a QuickLink web part programatically, with some already populated links, but I'm not able to find the proper way. I guess that we should fill the "items" property with some specific values, but cannot find which ones.

Some support would be appreciated. Thanks!

Chris O'Brien said...

@Txe,

Try adding that web part to your workbench page (in a tenant, not locally). Add some links, and then use the ***Web part data button. This will give you the data as the web part expects it (put it through a JSON decoder to read it more easily).

Hope that helps!

Cheers,

COB.

John Charles said...

Excellent Chris - Thank you for the detail!

//John