SPFx Form Customizer Extension To Customize SharePoint New/Edit/Display Form Of List/Libraries

Microsoft has recently released SPFx 1.15.1 Preview version adding a few new features and updates for building SPFx-based solutions. We can find full release notes at the link. One of the major and important features coming with this version is the ability for users to customize new, edit, and display forms of lists and libraries. This has been la ong asked-for and awaited feature which we now have a way to achieve using SPFx.

Please note that as this is still in preview, it involves some manual steps to test the customizer. Also as of today, applying the customizer to list or content type is still in progress and we are developers would only be able to test and debug this and cannot deploy to actually list... This article is inspired by video from Vesa at the link - "Preview on upcoming list extensibility options with SPFx v1.15"

In this article, we will go step by step on creating a Form customizer and then test it by the local debugging experience of SPFx. 

Assuming you have already set up your SPFx environment, below are steps to be followed.

Step 1 - Install the latest preview version of SPFx

npm install @microsoft/generator-sharepoint@next --global

Step 2 - Create the targeted List 

For the sake of simplicity, we will create a list with only two columns, 

Title - Single Line of Text

Description - Multiple lines of Text 

Next, we will have to enable Content type, Go to list settings -> Advanced Settings -> Select Yes on "Allow management of content types?" and click on Save

In this step, we will also have to copy the List content type which we will need later. Now on the list settings, we will see Item content type added. Click on Item and then copy the content type id from url.

Copy this ctype value from the query string and keep it handy to be used later.

Step 3 - Create Project

Next, we will go ahead and create the SPFx project. Go to the command prompt and create a folder of your choice. Run below command

yo @microsoft/sharepoint

We will be asked a series of questions, choose the below options and continue.

Select component type as Extension and then type of extension as Form Customizer, this is a new option that you will find with this new release.

Once successful, we should see below screen

Step 4 - Modify the configuration(serve.json)

Next, we will modify the code and also do some configuration to associate the form customizer with the list and content type that we created and copied above.

Open the solution using "code ." from the root folder of the solution from the command prompt. it will open Visual studio code.

From the created folder structure find the serve.json file at config\serve.json

Here we will see, the attribute serveConfigurations and a few objects with default, new form, edit form, and view form...

The first thing we will do is replace the site URL with our target site URL where we have to test,

So replace the pageUrl property with our target site collection.

"pageUrl": "https://contoso.sharepoint.com/sites/mySite/_layouts/15/SPListForm.aspx" TO  "pageUrl": "https://mydomain.sharepoint.com/sites/Training/_layouts/15/SPListForm.aspx",

Then we have to replace RootFolder to targeted List relative path

"RootFolder": "/sites/mySite/Lists/MyList",  TO  "RootFolder": "/sites/Training/Lists/FormCustomizerDemoList",

Then we will have to replace ContentTypeID, which we would have noted in the step above.

"ContentTypeID": "0x0100" TO "ContentTypeID": "0x0100473DC3AA69DEE44EA7E837F83D80DFB200A715C1F3F81BC042BF936CEF4414F96D",

Replace this in all the serveConfigurations objects. Refer screenshot below.

Step 5 - Modify render Method 

Next, we will modify React component which is responsible for showing control when the form customizer is loaded.

Go to src\extensions\firstCustomizer\components\FirstCustomizer.tsx

Modify the render method to below

 public render(): React.ReactElement<{}> {
    return <div className={styles.firstCustomizer}>Hello World........</div>;
  }
}

Step 6 - Test the configuration 

Go to command prompt again and run 

gulp serve

It should open the browser with the below screen,

Click on Load Debug Scripts, if it loads correctly, we will see the below screen. 

Step 7 - Modify Code to Create a new Entry in the SP list

Now that we know the configuration is correct we will modify the Form code to add input controls and buttons and logic to save data to SP list. For saving data to SP list we will use pnpjs. 

Kill the gulp serve and run the below command to install a specific version of pnpjs

npm install @pnp/[email protected]

Please note we are using an older version of pnpjs for simplicity and reuse my old code :) 

Once it is installed, let us modify Go to src\extensions\firstCustomizer\components\FirstCustomizer.tsx.

Add a few imports which we need to use.

import { sp } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import { IStackTokens, MessageBar, PrimaryButton, Stack, TextField } from 'office-ui-fabric-react';

Replace render method to add controls

public render(): React.ReactElement<{}> {
    return <div className={styles.firstCustomizer}> 
       <TextField required onChange={evt => this.updateTitleValue(evt)} value={this.state.itemObject.title} label="Add Title" />
       <TextField required onChange={evt => this.updateDescriptionValue(evt)} value={this.state.itemObject.Desc} label="Add Description" multiline/>

      <br/>

      <Stack horizontal tokens={stackTokens}>
      <PrimaryButton text="Create New Item" onClick={()=>this.createNewItem()}  />
      <PrimaryButton text="Reset" onClick={()=>this.resetControls()}  />
    </Stack>
      
      <br/>
      {this.state.showmessageBar &&
             <MessageBar   onDismiss={()=>this.setState({showmessageBar:false})}
                dismissButtonAriaLabel="Close">
                "Item saved Sucessfully..."
            </MessageBar>
      }
    
    </div>;
  }

Create a State interface to hold values from controls, you can add below where IFirstCustomizerProps is declared.

//create state
export interface IFirstFormCustomizerState {
  showmessageBar:boolean; //to show/hide message bar on success
  itemObject:any;
 }

Add below line below state declaration 

const stackTokens: IStackTokens = { childrenGap: 40 };

Modify FirstFormCustomizer class to use State and then create a constructor

export default class FirstFormCustomizer extends React.Component<IFirstCustomizerProps, IFirstFormCustomizerState> {
  // Example formatting
  // constructor to intialize state and pnp sp object.
  constructor(props: IFirstCustomizerProps,state:IFirstFormCustomizerState) {
    super(props);
    this.state = {showmessageBar:false,itemObject:{}};
    sp.setup({
      spfxContext: this.props.context
    });
  }

Add below 4 methods to handle the logic to save state, save in sp list, and reset controls

private async createNewItem(){
    const iar: any = await sp.web.lists.getByTitle(this.props.context.list.title).items.add({
      Title: this.state.itemObject.title + new Date(),
      Description: this.state.itemObject.Desc
    });
    console.log(iar);
    this.setState({showmessageBar:true});
    //this.props.onSave();
  }

  private updateTitleValue(evt) {
    var item = this.state.itemObject;
    item.title = evt.target.value;
    this.setState({
      itemObject: item
    });
  }

  private updateDescriptionValue(evt) {
    var item = this.state.itemObject;
    item.Desc = evt.target.value;
    this.setState({
      itemObject: item
    });
  }

  private async resetControls(){
    var item = this.state.itemObject;
    item.title = "";
    item.Desc = ""
    this.setState({
      itemObject: item
    });
  }

Explanation about code

  • So here we are adding two controls Title and Description from the user and associating these controls with the state object to hold its value, onChange method on these controls takes care of saving the value to State.
  • Then we have added two buttons - one to Save and create a new items and the Reset button to reset the values in controls. 
  • Create new item button click will call the method createNewItem which uses PNP js to save the data from state to create a new entry in list.
  • If you would have noticed we have used this.props.context.list.title to target the list name, so here we are using the context object from FormCustomizer which gives us objects and context on which the form Customizer is running.
  • There are 3 important attributes that would be important for developers to 
    • this.props.displayMode -  this will help us identify if the form is in new, edit or display mode and we can use this to control our logic, please note as we are debugging using gulp serve for now this will always be new  

    • this.props.context.list - this provides us the list object on which the form customizer is running. it has 3 attributes which are list title, list GUID and list server relative url.

    • this.props.context.itemId - this will provides us itemId on which current form is opened, this will be applicable when displayMode would be "Edit" or "Display"

Step 8 - Test the latest code

Run gulp serve again and click on Load debug script on the browser once opened

We should see the below output

Enter some value in Title and Description and click on Create new item

Go to the list and see a new entry is created

This will conclude our First Demo on Form customizer with SPFx, you can refer to the source code at this repo 

We're waiting for the release of the new feature when we will be able to assoicate this with actual Lists. It will be interesting to see how this will work out and the experience when user clicks on New Item, Edit Item, etc... 

Hope this helps and Happy coding..!!!1