Thumbnail Building a SharePoint New Site Form Look-Alike

Building a SharePoint New Site Form Look-Alike

Allowing users to create new sites is a common scenario in SharePoint. And yet, a lot of organizations disable self-service site creation for users. Instead they want to control the creation of new sites. Possibly with a custom form that allows users to request a new site, with approval processes in place. This post shows how to get started doing that.

Say we’d like to build a custom form to have users request new sites. Say you want to make it look like the default SharePoint new site form, and say you want to extend that experience? You can go about this in a number of ways. For this post I’ve used a simple SharePoint list combined with a SharePoint Framework Form customizer, a Dynamic Form control and some field overrides. The end result looks like this:

dynamic-site-form gif

I’ve submitted my code to the SPFx extensions samples repository, so go and check it out, if you want to see how it works in depth. It’s currently still in PR-stage, it’s not merged yet. I’ll update this post as soon as it is published. I’ve also submitted a simpler example of using Dynamic Forms at this PR.

What I used to create this sample is a SharePoint Framework Form Customizer. Form Customizers are a way to override the new form, edit form and/or view form of a SharePoint list or library with custom code. It’s a nice way to build tailored form experiences using the SharePoint Framework and client side coding.

The only drawback of a Form Customizer is… well, that you need to build almost everything yourself. As soon as you connect a Form Customizer to a list, the entire form is empty, and you need to implement everything, from retrieving the list item, to rendering the fields, to saving the item. Everything!

That’s where the Dynamic Form control comes in. It’s an open source control of the PnP SPFx Controls repository that allows you to render a list form. It’s a great way to get started with building a custom form. The Dynamic Form renders the fields, loads the list item (on edit and view forms) and saves the updated item back to the list.

…and you can extend them!!

💍 This is why I think Dynamic Form controls and Form Customizers are a match made in heaven!

🎈Sharing is caring: Incidentally, the actual functionality that allows you to extend a Dynamic Form, has been contributed by my colleague and me. (see here and see here) Because sharing is caring!

To build this, I created a basic SharePoint list with the following fields:

  • Site name (The default ‘Title’ text-field)
  • URL (Hyperlink field)
  • GroupMailbox (Text-field)
  • Owner (Peoplepicker-field)
  • Members (Peoplepicker-field)

Only the first three are necessary for this solution, as they are referenced in code to be overridden. (Based on the internal name of the fields) You can add any other field you like. The Dynamic Form can render all field-types by default. This is just to show that such a form is easily made.

Our starting point is an empty react based Form Customizer, which you can get if you follow the get started guide. Once we have that, we can install the PnP SPFx react controls as a dependency:

npm install @pnp/spfx-controls-react --save-exact

Once we have the list and the SPFx Form Customizer in place, we can render the list form as follows:

<DynamicForm
    context={props.context as never}
    listId={props.context.list.guid.toString()}
    listItemId={props.context.itemId}
    onCancelled={props.onClose}
    onSubmitted={props.onSave}
    disabled={props.displayMode === FormDisplayMode.Display}
/>

As you can see, this will use the existing properties that a Form Customizer has. The context, containing the List information, and the functions for saving and cancelling. This will render the list form, and basically give us a fully functional form with which we can create new items and update existing ones.

It’s that easy!

To override the dynamic form, we add a few extra properties:

<DynamicForm
    ...
    fieldOverrides={fieldOverrides}
    onListItemLoaded={onListItemLoaded}
    onBeforeSubmit={onBeforeSubmit}
/>

We can implement the fieldOverrides property by adding a function that returns a react element for each field that we want to override (referenced by the internal field name):

const fieldOverrides = {
    "Title": (fieldProperties: IDynamicFieldProps) => <>...</>,
    "GroupMailbox": (fieldProperties: IDynamicFieldProps) => <>...</>,
    "URL": (fieldProperties: IDynamicFieldProps) => <>...</>
};

The fieldProperties is passed from the Dynamic Form and contains all information for a field, such as the field title, if it’s required, the default value, etc.

In this sample we are overriding some fields and doing some additional logic on the values, so we need to use the state of the react component. The onListItemLoaded callback is run right after the list item data is loaded on the edit and view-forms. We use it to set relevant properties in the state.

For the same reason, we need to use the onBeforeSubmit callback. This is run right before the list item is saved, which gives us the possibility to set the fields with the correct values from the state.

const [siteUrl, setSiteUrl] = React.useState<string>();

// Use onListItemLoaded to get list item info into the component state.
async function onListItemLoaded(listItemData: ISiteListItem): Promise<void> {
    if (listItemData === undefined) {
        return;
    }
    
    // ...we can do some additional logic here.
    setSiteUrl(listItemData.URL.url);
} 

// Use onBeforeSubmit to merge the component state with the list item information.
async function onBeforeSubmit(listItemData: ISiteListItem): Promise<boolean> {
    // ...we can do some additional logic here.
    listItemData.URL = { Url: siteUrl, Description: '' }

    return false; // returning 'true' would cancel the form saving.
}

As you can see I’m using a React Functional Component, but this works equally well with class components. So the basic idea is that we tap into the extensibility points of the Dynamic Form to override the way the fields are rendered and enable ourselves to use the component state to do some additional logic when loading and saving list items.

To make this form look like the default SharePoint new site form, we need some way to check if a site-url and a group mailbox are available. I just checked using the browser dev tools, and it appeared to be quite easy. Although we need to remember that the default SharePoint form uses some non-public API’s. These might change in the future without Microsoft notifying us, so we need to be aware of that.

The following endpoint can be used to check the availability of a site url:

GET <siteUrl>/_api/GroupSiteManager/GetValidSiteUrlFromAlias?alias='sales'&managedPath='sites'

The endpoint returns a URL that you can use, based on the alias. If the URL you’re requesting is already used, it will returned a modified URL. In the above example: if /sites/sales is already used, it might return /sites/sales2. In our form we can check whether the URL is returned as we would expect and if not we can show a message saying the URL is not available, except in a modified form.

The following endpoint can be used to check the availability of a group mailbox:

GET <siteUrl>/_api/SP.Directory.DirectorySession/ValidateGroupName(displayName='Sales',alias='sales')

This actually checks for two values: the site name and the group mailbox, although I have never succeeded in triggering an error on the display name value. The endpoint returns a response object that can contain error information:

{
    AliasErrorDetails: null, // possible error details
    DisplayNameErrorDetails: null, // possible error details, never seen though
    ErrorCode: null, //eg: "Request_UnprocessableEntity";
    ErrorMessage: null, //eg: "The values provided contain one or more validation errors.",
    IsValidName: true
}

By using these two endpoints we can mimic the SharePoint default site form with great precision.

The Dynamic Form is a great way to create a form without a lot of fuss, and extend it where necessary. I hope you get my drift here, and if you’re interested in knowing more, do check out the link to the code sample that I’ve submitted to the samples repo.

Happy coding!


spfx sharepoint
Support me by sharing this

More

More blogs

Extending Microsoft 365 with custom retention controls
Extending Microsoft 365 with custom retention controls

Thinking about a Purview post by Joanne C Klein, I've developed a small Microsoft 365 extension to view & manage retention controls.

Read more
Debugging production code in SPFx
Debugging production code in SPFx

A way to debug minified SPFx code in production against your original typescript code.

Read more
How to copy views in SharePoint
How to copy views in SharePoint

There's currently no way to copy list and library views in the SharePoint UI. I've created a SPFx sample demonstrating how it can be done using code.

Read more

Thanks

Thanks for reading

Thanks for reading my blog, I hope you got what you came for. Blogs of others have been super important during my work. This site is me returning the favor. If you read anything you do not understand because I failed to clarify it enough, please drop me a post using my socials or the contact form.


Warm regards,
Martin

Microsoft MVP | Microsoft 365 Architect

Microsoft MVP horizontal