Introduction
SharePoint Framework offers various possibilities, one of those is the ability to create an extension that will enable the developer to extend the command bar in a SharePoint list.
To show you what I mean I’ve created a sample solution straight from the default solution that is created with the SharePoint Framework Toolkit. Starting from there I’ve did some changes adding custom icons and some custom text, if you’re interested in viewing the code you can find it here.
Starting with the resulting UI there are a couple of commands in this sample. The “List favorites” one will be shown always:
And when clicking on the command icon a dialog will be opened:
The second command, the “Add to favorites” one, will only be visible when an item is selected:
Also this command will display a dialog when clicked:
Let’s cover a little bit more about how to create the ListView command set and how it’s composed.
Creating a new solution
There are a couple of different fashions to create a ListView command set extension solution and those are:
- using the SharePoint Framework Toolkit.
- using the Yeoman generator.
SharePoint Framework Toolkit
The SharePoint Framework Toolkit is an awesome Visual Studio Code extension that helps the developers to create and manage their SPFx solutions. This extension is a great UI tool and allows you to create a new ListView command set extension.
For example the project creation page is the following:
As you can see there is the selectable option to create an extension component and also the type of component to create, which in this case is the ListView Command Set.
Yeoman generator
Using Yeoman is possible to scaffold a SPFx solution using a command line interface. If you’re interested in knowing more you can have a look at one of my previous blog post.
To have an idea of what the tool looks like here is an example of a prompt for creating a new ListView command set solution:
Manifest.json
The ListView command set extension rely on the JSON manifest for defyining the available commands and their representation in the UI.
The manifest file is named in the following fashion [your extension name].manifest.json
and you can find it in the same folder of your extension class.
In the sample, my folder structure and manifest name looks like the following:
In the manifest we have, alongside the definition of our extension, the command list items that we will be using. In the sample solution the manifest is something like the following:
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/command-set-extension-manifest.schema.json",
"id": "<guid for your extension>",
"alias": "BasicListViewCommandSet",
"componentType": "Extension",
"extensionType": "ListViewCommandSet",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"items": {
"ADD_TO_FAVORITES": {
"title": { "default": "Add to favorites", "it-IT": "Aggiungi ai preferiti", "fr-FR": "Ajouter aux favoris" },
"iconImageUrl": "<omitted for brevity>",
"type": "command"
},
"LIST_FAVORITES": {
"title": { "default": "List favorites", "it-IT": "Elenco preferiti", "fr-FR": "Liste des favoris" },
"iconImageUrl": "<omitted for brevity>",
"type": "command"
}
}
}
As you can see, inside the items
property there is a key-value structure for each of the commands used.
Taking the following as example:
"ADD_TO_FAVORITES": {
"title": { "default": "Add to favorites", "it-IT": "Aggiungi ai preferiti", "fr-FR": "Ajouter aux favoris" },
"iconImageUrl": "<omitted for brevity>",
"type": "command"
},
The string value ADD_TO_FAVORITES
will be the command name. The command contains an object to represent it in the UI, the properties in details are:
-
title
: is an object with strings representing the title of the command. Other than thedefault
string is possible to define localized strings. For each localized string is enough to specify the localization code and the localized string. -
iconImageUrl
: is the URL to the image that will be used as the command icon. The property supports a base64 string representing the image to use. It’s recommended to use an SVG file for the best result possible. The size of the icon will be 16 x 16 pixels. -
type
: will becommand
for a command object.
Show me the code
When the solution is created a bunch of imports are defined:
import {
BaseListViewCommandSet,
type Command,
type IListViewCommandSetExecuteEventParameters,
type ListViewStateChangedEventArgs
} from '@microsoft/sp-listview-extensibility';
By default there is a property interface defined, I’ve updated it to stick with my example:
export interface IBasicListViewCommandSetProperties {
AddToFavoritesText: string;
ListFavoritesText: string;
}
I’ve defined the commands as constants just to avoid writing the strings everywhere and having a single place where I defined those strings:
const ADD_TO_FAVORITES: string = "ADD_TO_FAVORITES";
const LIST_FAVORITES: string = "LIST_FAVORITES";
The ListView command set class extends the BaseListViewCommandSet
which is the base class for the extension:
export default class BasicListViewCommandSet extends BaseListViewCommandSet<IBasicListViewCommandSetProperties> {
The base class offers different methods that can be used, let’s cover those.
onInit
The onInit
method is called first and there you can perform the initial setups. In this method is also possible to customize some of the command properties.
Let’s see the code of the sample:
public onInit(): Promise<void> {
// initial state of the command's visibility
const addToFavoritesCommand: Command = this.tryGetCommand(ADD_TO_FAVORITES);
addToFavoritesCommand.visible = false;
this.context.listView.listViewStateChangedEvent.add(this, this._onListViewStateChanged);
return Promise.resolve();
}
Here the code is used to retrieve the ADD_TO_FAVORITES
command and set it’s default visibility to false
since this command will be visible only when selecting a list item.
Other than the command visibility, the onInit
method, is also used to register the _onListViewStateChanged
used to perform operations when the list view state will change.
onExecute
This method is used when a click is performed on one of the commands.
The argument passed to the method is of type IListViewCommandSetExecuteEventParameters
, this interface exposes two properties:
-
itemId
: is the identifier of the command. -
selectedRows
: represents an array of the selected rows with the list view fields and the list item values.
public onExecute(event: IListViewCommandSetExecuteEventParameters): void {
switch (event.itemId) {
case ADD_TO_FAVORITES:
Dialog.alert(`${this.properties.AddToFavoritesText}`)
.catch(() => {
/* handle error */
});
break;
case LIST_FAVORITES:
Dialog.alert(`${this.properties.ListFavoritesText}`)
.catch(() => {
/* handle error */
});
break;
default:
throw new Error("Unknown command");
}
}
In the sample solution the commands do nothing in particular other than showing a dialog with a message. In a real world scenario this is the place where you would insert your business logic.
_onListViewStateChanged
This method, after binding it in the onInit
method, will be called when the list view will be updated.
Here you can handle the changes that will be made based on the list view state. One example is the handling of the command visibility.
Remember to call the raiseOnChange
method at the end!
private _onListViewStateChanged = (args: ListViewStateChangedEventArgs): void => {
const addToFavoritesCommand: Command =
this.tryGetCommand(ADD_TO_FAVORITES);
if (addToFavoritesCommand) {
addToFavoritesCommand.visible =
this.context.listView.selectedRows?.length === 1;
}
// Call the following to update the command bar
this.raiseOnChange();
}
Test your extension
Is possible to test your extension using gulp. To test the extension it will be enough to execute the following command in the root folder of your solution:
gulp serve
However before doing so it would be better to have a look at the serve.json
file. The serve.json
file can be found under the config
folder placed in the root folder of your solution.
The serve.json
file enables to define the URL/s where you want to test your solution. An example of the file from my sample solution is the following:
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
"port": 4321,
"https": true,
"serveConfigurations": {
"default": {
"pageUrl": "<the URL to your SharePoint site list>",
"customActions": {
"<id from the manifest.json>": {
"location": "ClientSideExtension.ListViewCommandSet.CommandBar",
"properties": {
"AddToFavoritesText": "Item added to favorites",
"ListFavoritesText": "Here should be the list of favorite items."
}
}
}
},
"basicListView": {
"pageUrl": "<the URL to your SharePoint site list>",
"customActions": {
"<id from the manifest.json>": {
"location": "ClientSideExtension.ListViewCommandSet.CommandBar",
"properties": {
"AddToFavoritesText": "Item added to favorites",
"ListFavoritesText": "Here should be the list of favorite items."
}
}
}
}
}
}
There are a couple of placeholders that I’ve inserted in the previous code section. Those are just to explain a couple of things, let’s cover those briefly.
Each of the serve configuration objects contains a pageUrl
property. The value of the pageUrl
property is the address of the page where you want to debug your extension. By default the value set is the following:
"pageUrl": "https://{tenantDomain}/SitePages/myPage.aspx",
You should know that, by default, the {tenantDomain}
placeholder is inserted in the pageUrl
property value. The placeholder will be replaced while using gulp serve. If you want to know more about this you can have a look at the documentation here.
Once the serve.json
is set and the gulp command is executed a page will be opened. In the page that will be opened you will be requested if to load or not the debug scripts. To debug your extension simply click on “Load debug scripts”.
And now you’re ready to debug your new list view command set extension.
Conclusions
Now you have a basic understanding of the list view command set extension. The extension offers an awesome customization of the SharePoint UI in a simple and convenient fashion.
If you want to know more about this extension I suggest you to have a look at the official documentation here.
Hope this helps!
Top comments (0)