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:
- Ability to add a custom SPFx web part to a page programmatically (or in some other provisioning sense e.g. PnP provisioning XML)
- Ability to add one of Microsoft’s out-of-the-box SPFx web part to a page programmatically (or with PnP provisioning XML)
- Ability to automatically add an app to a site, so that any SPFx web parts (or other artifacts) are available
- Ability to create modern pages programmatically
- 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:
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:
- 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
- Install the https://www.nuget.org/packages/SharePointPnPCoreOnline NuGet package, using a version which is later than 2.13.1703 (March 2017):
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:
Incidentally, that method allows you to choose between all of the currently-available out-of-the-box web parts using an Enum:
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:
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):
..or an easier way can be to instantiate the web part first using the PnP method, and look at that in the debugger:
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:
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();
..anyway, now my custom web part is provisioned to the page (with my truly beautiful color theme in this case!):
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:
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:
As always, excellent post, will keep an eye on this as I am sure I will need it in the future developments.
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!
@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.
Excellent Chris - Thank you for the detail!
//John
Post a Comment