Creating Web Part that Support Single Part App Pages in SharePoint Online

I’ve been working with the SharePoint Framework (SPFx) development method for a while now and I must admit that whilst I was sceptical at first (yet another way of extending SharePoint to learn) I am now a believer. This is where Microsoft are pouring their investment dollars and so if you are a SharePoint developer you should take note and get on board with this new (ish) way of doing things. SPFx Web Parts are cool and whilst coming from a traditional C# developer background meant that I had to get to grips with TypeScript and other client-side technologies such as React, I think this is a necessary and inevitable, if somewhat painful, transition.

I’ve recently been working on the development of a new web part which I call Kaboodle Scaffold. The idea is that it provides a structure navigation experience for users to access documents of any kind including web pages, PDFs, Office formats, OneNote and multimedia files. It’s quite possibly one of the coolest things I’ve built. To whet your appetite, the screenshot shows the Scaffold prototype.

The idea is that a web part instance hooks into a JSON file which provides a hierarchical navigational structure on the left and when users click on a document node, the resource gets loaded into the iFrame on the right. I foresee many use case scenarios for this including:

  • Navigating Managed Documents: Users could access Policy documents within a business area and then drill down into Procedures, SOP, Guidelines etc. And then there might be Forms that relate to specific Procedures.
  • Training Resources: Users could access related training materials and resources.
  • Wiki Replacement: A model might be constructed as an overlay or replacement for a knowledge base or wiki.
  • Staff Onboarding: This could be used as part of a staff onboarding or induction system.
  • Technical Documentation: Instead of presenting users with a massive tomb they might better find what they need through structured navigation.

Now the thing is, to get best use out of this sort of solution I’d really want my web part to take up more screen real estate than SharePoint is normally willing to hand over. Afterall, the idea behind a web page (Modern or Classic) is that it can host multiple web parts in different sections, columns or zones. But in this case, what I really want is a page with a single purpose in mind, namely to host my document viewer.

Well it turns out you can do this with the SPFx by building what Microsoft call a Single Part App Page.

In this post I will walk you through the basics of how to create a Single Part App Page application and then show you how to extend a web part with a custom button that will allow users to switch between page modes. This means that users won’t need to run PowerShell or resort to other ugly mechanisms to configure a page as we will empower end users to do it themselves!

What’s a Single Part App Page?

So, as the name implies, it’s a SharePoint Site Page that has been specifically configured to host just a single Modern client-side web part. How we make this happen is a game of 2 halves:

  • Configure the Web Part: The web part you are developing needs to be configured to support being used in a single part app page.
  • Configure the Hosting Site Page: The page that is hosting our web part has to be told to behave like a single part app page.

Microsoft provide some fairly straight forward documentation on how to do this at:

https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/single-part-app-pages.

Configuring the Web Part

The first part is almost trivial but I will create a standard SPFx web part (no additional frameworks required) to highlight what needs to be done, mainly because we need a vehicle to explain and demonstrate how I have gone about configuring the web part to support the 2nd part of the equation.

I’ve just used Yeoman to scaffold a new web part project. If you not familiar with how to do this then feel free to start you journey with my earlier post Adventures with the SharePoint Framework (SPFx): Part 1 – Getting Started.

The screenshot below shows the parameters I used to set up the project.

10 minutes later and you can gulp serve your project to the local workbench and add web part instance to check that all is well.

Next, open up you project in VS Code and dig into the web parts manifest.json file. In the supportedHosts section add a new text value, “SharePointFullPage” as highlighted below.

The only other change I made was to the web part module.scss file. In here I just commented out the max-width attribute and added a width attribute set to 100%. This is just so we can actually see the difference when we switch the mode of the hosting site page.

At this point we’d normally do something clever with the web part and test it out in the local workbench or if we need access to real SharePoint data then we’d use a remote workbench in some SharePoint tenancy we presumably have at our disposal.

But none of that is going to help us here as we need the web part to be hosted on a real Site Page and not a workbench in order to see what our creation will look like as a Single Part App Page. We therefore need to:

gulp bundle – -ship

and then

gulp package-solution – -ship

the solution to spit out our sppkg file.

This latter command will create a SharePoint folder in the folder structure and generate the solution package file which you can then accessed in Windows Explorer.

Next, we need to upload this to the App Catalog in the tenancy you are using to test this thing out.

You will of course need to trust and deploy the solution

Then find you way to some test site and install the solution package by adding the app to the site.

Now add a new Site Page and add an instance of you web part to the page.

And marvel at you smarts.

Notice how the web part content now takes up 100% of the column in which it was added, rather than being constrained to the 700px max-width that would normally be the case but can you also see that the web part is not taking up the full width of the page which is what we are actually after.

So now we come to the 2nd half and that’s to configure the page to be a Single Part App Page.

Configuring the Hosting Site Page

If you refer to the Microsoft article mentioned above, they provide 3 methods of configuring the page. Later on, I’ll introduce a 4th method but for now let’s follow along with the Microsoft endorsed approaches.

Of the 3 identified methods, the method that runs JavaScript in the bowser’s developer tools did not work for me (I was using Edge). Despite there being a missing semi-colon on the 2nd line of the sample script, SharePoint just spat the dummy. It helpful stated that it understood my commands be refused to execute them!

I don’t bother trying method 3, the Office 365 CLI, which I’d not seen before and I presume stands for Command Line Interface, instead I took the middle ground with PnP PowerShell and ran the following:

Connect-PnPOnline -Url https://{your domain}.sharepoint.com/{your site path}

$item = Get-PnPListItem -List “Site Pages” -Query “<View><Query><Where><Eq><FieldRef Name=’FileLeafRef’/><Value Type=’Text’>{your app page file name}.aspx</Value></Eq></Where></Query></View>”

$item[“PageLayoutType”] = “SingleWebPartAppPage”

$item.Update()

Invoke-PnPQuery

With the appropriate values specified for your domain, site path and web part page file name of course You’ll likely be promoted for credentials.

And hey presto, our web part now fills the page

A few things to call out here:

First, the header which was on the original page is gone but its hidden rather than removed.

Second, despite what I read in most of the stuff on line, you can in fact edit the web part properties.

This seems to be the case with custom web parts but if you try and convert a page which hosts a standard OOTB SharePoint web part then you do indeed lose the ability to customise any properties. I strongly suspect that setting the supportedHosts flag is what makes the difference here. The OOTB web parts were build long before this Single Part App Page concept was conceived. I’m thinking that any custom web parts that don’t have the flag set will behave in a similar way, but I haven’t checked this out to verify it.

If you are converting a page with standard web parts (or web parts not flagged as supporting Single Part App Pages) then the process is to first configure the web part as you would like it and then convert the page to a Single Part App Page. If you need to update the web part then you’d have to revert it to a standard Site Page, configure the web part as required and then set it back again afterwards which is a bit of a hassle.

Talking of which, the Microsoft article fails to tell you how to revert the page back to a normal site page. It turns out that you just have to run the same PowerShell put this time set the PageLayoutType to “Article”:

$item[“PageLayoutType”] = “Article”

You also need to be aware that if you try and convert a page that contains more than one web part, then SharePoint uses the first web part it come across and any other web parts are ripped from the page and deleted. Whilst the page header is restored if you revert the page back to an article page the same is not true of any other web parts on the page, they are removed, never to be seen again!

Empowering Users

So, there we have it, the prescribed way to configure a web part and set up a Site Page to host it as a Single Part App Page. The process is fairly straight forward but there is one thing that I really hate and that is, to set things up, I need to run PowerShell (or some complex script, even if it worked). This is not something we can reasonable expect users to do, even if we were to give then the power to do so (heaven forbid). Surely, we can find a way to empower users to make it happen at the click of a button instead of having to find a friendly administrator – is that an oxymoron?

Well, it seems I’m not the only one who thinks so. Luis Manez has posted a community demonstration on YouTube showing how he build a SPFx command extension to extend the UI of the Site Pages library to let users take control. His approach is a sound one but I want to take a different tac. I don’t want to make the transformation process reliant on another app but rather make it a feature of my web part. The idea is that when a user adds a web part instance to the page, they can click on a button in the web part properties to set the current page (they one hosting the web part) to be a Single Part App Page or revert it to being a standard Article page at will.

Let’s make that happen.

Adding a Command Button

At the time of writing, the PageLayoutType property of a Site Page can be set to one of the following 4 text values:

  • “Article”: A standard site page or new article.
  • “Home”: The homepage of the site.
  • “RepostPage”: A Repost Page, which is essentially a link to another page.
  • “SingleWebPartAppPage”: A Single Web Part App Page

What we need is a way to detect the value of the current page and then switch it over to a different mode at the click of a button.

We obviously need is a button that the user can click in order to switch between page modes but I also want a custom enumeration type to track the button status. To set that up I’ve made a few changes to the web part code.

First, I added references to the PropertyPaneButton class and PropertyPaneButtonType enumeration type. I need these to be able to add a button to the web parts configuration pane.

Then I define a new enumeration type PageLayoutButtonMode which will be used track the state and determine what action (if any) is permitted based on the current value in the PageLayoutType property. Basically, if the page is already configured to be a Single Web Part App Page then the allowable action will be to convert back to an article page. If the page is configured as an article page or the home page of the site then we should allow the user to convert it into a Single Part App Page. Otherwise, if we can’t read the value for some reason (like when we are debugging in a workbench), we’ll want to disable the button.

I added a couple of properties to the web part interface, one to track the button mode and the other to store the text that will be displayed on the button as we’ll want to show different text depending on the button mode. I set the initial values for these properties in the preconfiguredEntries section of the web part’s manifest.json file as highlighted below.

Then in the getPropertyPaneConfiguration method I add a new PropertyPaneButton and set its properties as shown below.

Note how the onClick handler for the button is bound to the onPageLayoutButtonClick method. This method does nothing at the moment but you need to bind it to “this” so that that the context of the web part, and not the button itself, is accessible in the event handler.

Plumbing’s done, now the wiring

First up, I need to read the current value of the PageLayoutType property for the web page on which the web part resides. To make life easier I’m using the PnP/JS library which can be installed using the Node Package Manager at the command line in VS Code, just be running:

npm install @pnp/logging @pnp/common @pnp/odata @pnp/sp –save

Don’t forget to install the Pollyfills package if you need your web part to work with IE11.

You can then use the easy way to set up the PnP JS context by importing the setup and overriding the onInit() method.

Whilst I am at it, I added references to Environment and EnvironmentType. I’ll use these to check to make sure we’re in a SharePoint site page and not classic page or the local workbench, and a reference to the sp entry point into the PnP framework.

Next, I’m adding a couple of helper properties to the web part which will return the list item ID of the current page and the site relative URL of the site pages library. Note that when in the workbench, the listItem and list objects of the pageContext will be null and that makes sense because the web part is in the workbench and not on a real site page. We’ll make use of that in a moment.

The ConvertToSiteRelativeUrl method is just a helper that I’d normally push out to a static utilities class and is shown here just for completeness. This method does what is says on the tin and takes the absolute URL of the web site and turns that into a Server Relative URL from which we can easily get the URL of the site pages library which we will need shortly. I prefer to access the site page library this way rather than by library title because the title can be changed at any time and so would break the code but changing the URL is a lot harder to do and so this method of accessing the library is more robust.

Pulling this together we need a method that will read the PageLayoutType property and set the other properties of the web part which will drive how the button is to be configured.

First, we ensure that the button text value is set to some default value and then check the Environment to make sure that we are in a Modern SharePoint Site Page. If we are not then we simple set the button mode to Disabled and we are done. Note that we also need to check the list property of the pageContext because if that is null then we can assume that we are in the context of a workbench and so we’d want to disable the button in that case as well. If we are in a remote workbench then the Environment type will still be SharePoint but the list context will be null so we have to check both elements.

If we are in a Modern SharePoint site and we have a list context then we can use the PnP library to get a reference to the PageLayoutType value of the current page. The returned item is actually an object but the value we need can be accessed as a string from the PageLayoutType property of the returned object which I then assign to a pageLayoutType local variable.

This string value is then used to set the mode and text of the button. Note that we need the call to refresh the property pane or else the new values we have just assigned won’t be updated in the UI.

But from where do we call this method? Well we could call it onInit() I guess but that would mean the code gets called every time the page and web part gets loaded and we only really need this code to run when configuring the web part properties. So instead, we can override the onPropertyPaneConfigurationStart method and call our custom method from there.

If I now bundle, package and redeploy the web part you will see that on a standard article site page the button lights up to offer the possibility of converting the page into an application page.

Conversely of on a Single Part Application Page the button will tell us we can revert to an Article page.

And when we are in a workbench the button is disabled, which is what we want.

However, clicking the buttons does not currently do anything!

Setting the PageLayoutType Property

The final piece of the jigsaw is to update the value of the PageLayoutType property to the appropriate value when the user clicks the button.

The onPageLayoutButtonClick event handler has now been updated to toggle the PageLayoutType property. If the current layout is for a Single Web Part App Page then we set it to an Article page else we set it to be “SingleWebPartAppPage” knowing that the button will only have been enabled, and so the code can only run, if the PageLayoutType is “Home”, “Article” or “SingleWebPartAppPage” and that we have a valid Site Page context.

After applying the changes, we need to call the configurePageLayoutButton method so that the button gets set to the new and correct state.

That’s it. Bundle, Package and redeploy.

Note that when you click the button the button text will toggle to opposite value but because the button does not cause a full-page post back you will not see the effect until the page is reloaded

Kaboodle Scaffold Revisited

By making the site page of a Communication Portal site a Single Part App Page you can see that this approach lends itself to single page apps rather well. The web part sucks up all the available space on the canvas making it clear that this is a single page application.

In Summary

The ability to create a Single Part Application Page is a welcome addition to the arsenal of what you can do with the SPFx. There are certain use cases and scenarios in which you would want to dedicate a page to a single specific purpose. My, soon to be released, document viewer is a prime example and this is was what drove me to explore this capability in the SPFx.

It works well and it is very simple to adapt your custom web parts so that they can be used as Single Part Application Pages. Just remember that OOTB web parts or web parts that have not been flagged as supporting the Single Part App Page model will not expose their properties for user configuration.

What doesn’t work so well is the reliance on PowerShell or other non-technical-user techniques to change the layout of a Site Page. It’s not hard but it does make this job something that an administrator will be doing and not an end user.

However, the main purpose of this post was to demonstrate that it is fairly easy to adapt a web part to empower users. This make sense for web parts that you know will generally benefit from operating in a single part page mode.

Please ping me or leave comments if you have any questions or constructive feedback.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s