Giuliano De Luca | Blog | delucagiuliano.com SharePoint Framework is in General Availability from several months, Microsoft has spent a great effort to improve and driving the end users towards the modern UI on SharePoint Online, with amazing results from my point of view.

As I mentioned in the previous article, Microsoft has introduced SharePoint Framework Extensions, this allow the developers to extend the capabilities of the modern UI, through the following new extension types:

  • Application Customizers
  • Field Customizers
  • Command Sets

In this article I’ll consider the “Field Customizers”, with this extension type is possible to improve the user experience in a list view, modifying the UI during the rendering. I developed a SharePoint Framework Extensions solution using the robust front end framework Office UI Fabric, that offers many components (React, Javascript, Angular…..), for my scope I used the Toggle component. The great benefit that my solution offers, is the possibility to modify inline without open in edit mode the list item, the toggle component is disabled if the user has not enough permission to edit the item. I used also the amazing pnp js core library to perform rest api call, towards SharePoint, I encourage you to use this library developed by an amazing community. Here’s a quick look at the pnp js core library code:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Log } from '@microsoft/sp-core-library';
import { override } from '@microsoft/decorators';
import {
CellFormatter,
BaseFieldCustomizer,
IFieldCustomizerCellEventParameters
} from '@microsoft/sp-listview-extensibility';
import { SPPermission } from "@microsoft/sp-page-context";
import pnp, { List, ItemUpdateResult, Item } from 'sp-pnp-js';
import * as strings from 'toggleStrings';
import Toggle from './components/Toggle';
import { IToggleProps } from './components/IToggleProps'
/**
* If your field customizer uses the ClientSideComponentProperties JSON input,
* it will be deserialized into the BaseExtension.properties object.
* You can define an interface to describe it.
*/
export interface IToggleProperties {
// This is an example; replace with your own property
sampleText?: string;
}
const LOG_SOURCE: string = 'ToggleFieldCustomizer';
export default class ToggleFieldCustomizer
extends BaseFieldCustomizer<IToggleProperties> {
@override
public onInit(): Promise<void> {
// Add your custom initialization to this method. The framework will wait
// for the returned promise to resolve before firing any BaseFieldCustomizer events.
Log.info(LOG_SOURCE, 'Activated ToggleFieldCustomizer with properties:');
Log.info(LOG_SOURCE, JSON.stringify(this.properties, undefined, 2));
Log.info(LOG_SOURCE, `The following string should be equal: "Toggle" and "${strings.Title}"`);
return Promise.resolve<void>();
}
@override
public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
// Use this method to perform your custom cell rendering. The CellFormatter is a utility
// that you can use to convert the cellValue to a text string.
const value: string = event.cellValue;
const id: string = event.row.getValueByName('ID').toString();
const hasPermissions: boolean = this.context.pageContext.list.permissions.hasPermission(SPPermission.editListItems);
const toggle: React.ReactElement<{}> =
React.createElement(Toggle, { checked: value, id: id, disabled: !hasPermissions, onChanged: this.onToggleValueChanged.bind(this) } as IToggleProps);
ReactDOM.render(toggle, event.cellDiv);
}
@override
public onDisposeCell(event: IFieldCustomizerCellEventParameters): void {
// This method should be used to free any resources that were allocated during rendering.
// For example, if your onRenderCell() called ReactDOM.render(), then you should
// call ReactDOM.unmountComponentAtNode() here.
ReactDOM.unmountComponentAtNode(event.cellDiv);
super.onDisposeCell(event);
}
private onToggleValueChanged(value: boolean, id: string): void {
let etag: string = undefined;
pnp.sp.web.lists.getByTitle(this.context.pageContext.list.title).items.getById(parseInt(id)).get(undefined, {
headers: {
'Accept': 'application/json;odata=minimalmetadata'
}
})
.then((item: Item): Promise<any> => {
etag = item["odata.etag"];
return Promise.resolve((item as any) as any);
})
.then((item: any): Promise<ItemUpdateResult> => {
let updateObj: any = {};
updateObj[this.context.field.internalName] = value;
return pnp.sp.web.lists.getByTitle(this.context.pageContext.list.title)
.items.getById(parseInt(id)).update(updateObj, etag);
})
.then((result: ItemUpdateResult): void => {
console.log(`Item with ID: ${id} successfully updated`);
}, (error: any): void => {
console.log('Loading latest item failed with error: ' + error);
});
}
}

Here the final result:

Giuliano De Luca | Blog | delucagiuliano.com

My solution is available on github: https://github.com/giuleon/react-field-toggle

Source code of this web part is also available on the official GitHub of Microsoft SharePoint Framework Extensions samples & tutorials: https://github.com/SharePoint/sp-dev-fx-extensions/tree/master/samples/react-field-toggle