[SPFx] Form Customizer Boilerplate
Since the v1.15 of SharePoint Framework, we can (finally) customize list forms! Until now, the only customizable options were through PowerApps or limited to switch fields or display / hide them.
But actually, the customization leads to a gap with the native display, as clicking on "New" / "Edit" / "Display" redirects you to a dedicated SPFx hosting page. Even if there're plans to make custom forms available through the well-known side panel, I though it could be interesting to give a starting point solution, that would less impact users experience 🙂
It's also interesting to remind the existance of the ETag, which prevents concurrency updates on existing list items.
Prerequisites
- An Office 365 (Dev) Tenant or a Partner Demo Tenant
- SPFx 1.15 package (at least) installed on the local machine
The whole project snippet is available here.
For this example, we also use the following libraries:
For the package deployment & Content Type component association, we use the PnP PowerShell module.
Deep dive in code
Form customizer declaration
First, let's have a look at the init method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 |
|
this.context
, as we know it, contains know properties (because it's inheriting from BaseComponentContext
) such as instanceId
, pageContext
,... But here as a FormCustomizerContext
, we can get info related to the list context:
list
contentType
folderInfo
itemId
item
(new to SPFx 1.16)domElement
We also have list form context info, such as displayMode
that let us know if we're in New, Display or Edit state (but this info is also in the URL as query parameter 😬).
So here, we are loading the list item if we're displaying an existing one, through the _loadItem
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Once our data loaded, we just have to push it to our Form customizer component!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Component
Now let's have a look at the component!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
In the return
statement of the render
method we got interesting stuff:
- the
form
tag is used to control the required fields with native HTML5 behavior - the
CommandBar
, theSeparator
and theBreadcrumb
controls placed here are for keeping consistency to the UX, as we're moving to theSPListForm.aspx
SPFx hosting page
Let's keep scrolling down!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
|
A few other things to point out:
- As the
SPListForm.aspx
hosting page needs context parameters (such as the PageType or the ID) to know what to display, the_onClickEditItem
event redirects from the Display mode to the Edit mode - the
_renderSaveButton
method renders aPrimaryButton
with thetype
attribute submit, which is crucial when working in aform
context (and not declared as such by default in the Fluent UI component) - the
_onSubmitSaveItem
event triggered by the submit button mentioned before contains theevent.preventDefault()
statement, in order to not redirect the user (because it has to be handled by an inherited event declared in theFormBoilerplateFormCustomizer
, we'll see about that later) - this method sends the updated form data to the host, which will perform the update operation and redirect the user to the list view or catching the error to display to him
Below the form customizer "as is" when initiated by Yeoman:
As we can see, the default display is really...empty! You can also see that the SharePoint App Bar isn't displayed here.
So the idea was to keep the same UI as the one we have in the list form:
Based on this, here's my proposal of Form customizer header:
Now, back to the FormBoilerplateFormCustomizer
host:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Here, we got the following:
- Cleaning the Person field info as SharePoint REST API expects a specific format when adding / updating it
- Depending if it's a new or existing item, we add the known ETag to the request
- When working locally on the component, using the
DEBUG
property is usefull, because when triggering thethis.formSaved()
, theSPListForm.aspx
hosting page redirects to the site home page instead of the target list, which could be frustrating
A React control to rule them all
Since PnP SPFx Controls version 3.10, there's a new control called DynamicForm, which could perfectly fit to SPFx latest feature. Let's give a try!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
As we can see, it's shorter than the first approach! With a "few" lines we have a ready-to-use Form customizer!
But we notice that:
- there's neither
CommandBar
,Separator
orBreadcrumb
components, since the Save and Cancel buttons are part of theDynamicForm
component, we can't hide them or overriding them - we can't handle the ETag here, as the component doesn't have a prop which allows us to specify it
- the saving and cancelling events are handled by the component (through the
onSubmitted
and theonCancelled
event props)
Th advantage of this component is that it handles the list context, so that the fields are displayed accordingly to its host (with associated configuration for each of them). But we're limited into the UI (even if there's a render overriding method called fieldOverrides
), especially if we want to keep existing parts like the CommandBar
.
Deployment
In order to deploy our Form customizer properly, below the script used to deploy the solution to the tenant app catalog and associate the component to the list content Type.
Info
The list used here is the Employee onboarding template provided by Microsoft when creating a list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
And that's it! From this boilerplate, you should be able to start playing with the Form Customizer!
Happy coding! 😄