diff --git a/docs/images/datatables-spfx-list-configured.png b/docs/images/datatables-spfx-list-configured.png index ef7eb20c2..5776107f7 100644 Binary files a/docs/images/datatables-spfx-list-configured.png and b/docs/images/datatables-spfx-list-configured.png differ diff --git a/docs/images/datatables-spfx.png b/docs/images/datatables-spfx.png index 4d1a37734..014875063 100644 Binary files a/docs/images/datatables-spfx.png and b/docs/images/datatables-spfx.png differ diff --git a/docs/spfx/web-parts/guidance/migrate-jquery-datatables-script-to-spfx.md b/docs/spfx/web-parts/guidance/migrate-jquery-datatables-script-to-spfx.md index 0467b8931..b7c4cdfba 100644 --- a/docs/spfx/web-parts/guidance/migrate-jquery-datatables-script-to-spfx.md +++ b/docs/spfx/web-parts/guidance/migrate-jquery-datatables-script-to-spfx.md @@ -1,7 +1,7 @@ --- title: Migrate jQuery and DataTables solution built using Script Editor web part to SharePoint Framework description: Migrate a SharePoint customization using DataTables to build powerful data overviews of data coming from SharePoint and external APIs. -ms.date: 08/19/2020 +ms.date: 01/22/2026 ms.localizationpriority: high --- @@ -9,8 +9,6 @@ ms.localizationpriority: high One of the frequently used jQuery plug-ins is [DataTables](https://datatables.net/). With DataTables, you can easily build powerful data overviews of data coming from both SharePoint and external APIs. -[!INCLUDE [spfx-gulp-heft-migration-wip](../../../../includes/snippets/spfx-gulp-heft-migration-wip.md)] - ## List of IT requests built using the Script Editor web part To illustrate the process of migrating a SharePoint customization using DataTables to the SharePoint Framework, use the following solution that shows an overview of IT support requests retrieved from a SharePoint list. @@ -92,17 +90,17 @@ The solution is built by using the standard SharePoint Script Editor web part. F $(document).ready(function() { $('#requests').DataTable({ 'ajax': { - 'url': "../_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title", + 'url': "https://yourtenant.sharepoint.com/sites/yoursite/_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title", 'headers': { 'Accept': 'application/json;odata=nometadata' }, - 'dataSrc': function(data) { - return data.value.map(function(item) { + 'dataSrc': function(data: any) { + return data.value.map(function(item: any) { return [ item.ID, - item.BusinessUnit, - item.Category, - item.Status, - new Date(item.DueDate), - item.AssignedTo.Title + item.BusinessUnit || '', + item.Category || '', + item.Status || '', + item.DueDate ? new Date(item.DueDate) : '', + item.AssignedTo ? item.AssignedTo.Title : '' ]; }); } @@ -137,6 +135,12 @@ Transforming this customization to the SharePoint Framework offers a number of b ### Create new SharePoint Framework project +Before You Begin: + +Create a SharePoint list named "IT Requests" +Add these columns with exact internal names: BusinessUnit (Text), Category (Text), Status (Choice), DueDate (Date), AssignedTo (Person) +Add sample data to test the solution + 1. Start by creating a new folder for your project: ```console @@ -314,17 +318,17 @@ The last step is to include the code that initializes the data table and loads t $(document).ready(function () { $('#requests').DataTable({ 'ajax': { - 'url': "../../_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title", + 'url': "https://yourtenant.sharepoint.com/sites/yoursite/_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title", 'headers': { 'Accept': 'application/json;odata=nometadata' }, - 'dataSrc': function (data) { - return data.value.map(function (item) { + 'dataSrc': function (data: any) { + return data.value.map(function (item: any) { return [ item.ID, - item.BusinessUnit, - item.Category, - item.Status, - new Date(item.DueDate), - item.AssignedTo.Title + item.BusinessUnit || '', + item.Category || '', + item.Status || '', + item.DueDate ? new Date(item.DueDate) : '', + item.AssignedTo ? item.AssignedTo.Title : '' ]; }); } @@ -369,10 +373,10 @@ The last step is to include the code that initializes the data table and loads t 1. Verify that the web part is working as expected in the command line by executing: ```console - gulp serve --nobrowser + heft start --nobrowser ``` -Because the web part loads its data from SharePoint, you've to test the web part by using the hosted SharePoint Framework Workbench. Navigate to **https://{your-tenant-name}.sharepoint.com/_layouts/workbench.aspx** and add the web part to the canvas. You should now see the IT requests displayed by using the DataTables jQuery plug-in. +Because the web part loads its data from SharePoint, you have to test the web part by using the hosted SharePoint Framework Workbench. Navigate to **https://{your-tenant-name}.sharepoint.com/_layouts/workbench.aspx** and add the web part to the canvas. You should now see the IT requests displayed by using the DataTables jQuery plug-in. ![IT requests displayed in a SharePoint Framework client-side web part](../../../images/datatables-spfx.png) @@ -483,17 +487,17 @@ Initially, the name of the list from which the data should be loaded was embedde $(document).ready(() => { $('table', this.domElement).DataTable({ 'ajax': { - 'url': `../../_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`, + 'url': `https://yourtenant.sharepoint.com/sites/yoursite/_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`, 'headers': { 'Accept': 'application/json;odata=nometadata' }, - 'dataSrc': function (data) { - return data.value.map(function (item) { + 'dataSrc': function (data: any) { + return data.value.map(function (item: any) { return [ item.ID, - item.BusinessUnit, - item.Category, - item.Status, - new Date(item.DueDate), - item.AssignedTo.Title + item.BusinessUnit || '', + item.Category || '', + item.Status || '', + item.DueDate ? new Date(item.DueDate) : '', + item.AssignedTo ? item.AssignedTo.Title : '' ]; }); } @@ -514,12 +518,12 @@ Initially, the name of the list from which the data should be loaded was embedde At this point, the bulk of the code is still written using plain JavaScript. To avoid build issues with the `$` jQuery variable, you had to define it as `any` type before the class definition. Later, when transforming the code to TypeScript, you replace it with a proper type definition. - As you've moved the contents of the **script.js** file into the main web part file, the **script.js** is no longer necessary, and you can delete it from the project. + As you have moved the contents of the **script.js** file into the main web part file, the **script.js** is no longer necessary, and you can delete it from the project. 1. To verify that the web part is working as expected, run the following in the command line: ```console - gulp serve --nobrowser + heft start --nobrowser ``` 1. Navigate to the hosted Workbench and add the web part to the canvas. Open the web part property pane, specify the name of the list with IT requests, and select the **Apply** button to confirm the changes. @@ -559,7 +563,7 @@ To function properly, TypeScript requires type definitions for the different lib ### Update package references -To use types from the installed type definitions, you've to change how you reference libraries. +To use types from the installed type definitions, you have to change how you reference libraries. 1. In the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and change the `import 'jquery';` statement to: @@ -575,7 +579,7 @@ To use types from the installed type definitions, you've to change how you refer ### Update main web part files to TypeScript -Now that you've type definitions for all libraries installed in the project, you can start transforming the plain JavaScript code to TypeScript. +Now that you have type definitions for all libraries installed in the project, you can start transforming the plain JavaScript code to TypeScript. 1. Define an interface for the IT request information that you retrieve from the SharePoint list. In the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and just above the web part class, add the following code snippet: @@ -612,7 +616,7 @@ Now that you've type definitions for all libraries installed in the project, you $('table', this.domElement).DataTable({ 'ajax': { - 'url': `../../_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`, + 'url': `https://yourtenant.sharepoint.com/sites/yoursite/_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`, 'headers': { 'Accept': 'application/json;odata=nometadata' }, 'dataSrc': (data: { value: IRequestItem[] }): any[][] => { return data.value.map((item: IRequestItem): any[] => { @@ -640,7 +644,7 @@ Now that you've type definitions for all libraries installed in the project, you 1. Notice how the AJAX request, to retrieve the data from the SharePoint list, is now typed and helps you ensure you're referring to correct properties when passing them into an array to DataTables. The data structure used by DataTables to represent a row in the table is an array of mixed types, so for simplicity it was defined as `any[]`. Using the `any` type in this context isn't bad, because the data returned inside the `dataSrc` property is used internally by DataTables. - As you're updating the `render()` method, you've also added two more changes. First, you removed the `id` attribute from the table. This allows you to place multiple instances of the same web part on the page. Also, you removed the reference to the `$(document).ready()` function, which isn't necessary because the DOM of the element where the data table is rendered is set before the DataTables initiation code. + As you're updating the `render()` method, you have also added two more changes. First, you removed the `id` attribute from the table. This allows you to place multiple instances of the same web part on the page. Also, you removed the reference to the `$(document).ready()` function, which isn't necessary because the DOM of the element where the data table is rendered is set before the DataTables initiation code. ### Update the Moment.js DataTables plugin to TypeScript @@ -651,7 +655,7 @@ The last piece of the solution that needs to be transformed to TypeScript is the ```typescript import * as $ from 'jquery'; - import * as moment from 'moment'; + import moment from 'moment'; /* tslint:disable:no-function-expression */ ($.fn.dataTable.render as any).moment = function (from: string, to: string, locale: string): (d: any, type: string, row: any) => string { @@ -675,11 +679,11 @@ The last piece of the solution that needs to be transformed to TypeScript is the }; ``` -1. You start with loading references to jQuery and Moment.js to let TypeScript know what the corresponding variables refer to. Next, you define the plug-in function. Usually in TypeScript you use the arrow notation for functions (`=>`). In this case, however, because you need access to the `arguments` property, you've to use the regular function definition. To prevent tslint from reporting a warning about not using the arrow notation, you can explicitly disable the `no-function-expression` rule around the function definition. +1. You start with loading references to jQuery and Moment.js to let TypeScript know what the corresponding variables refer to. Next, you define the plug-in function. Usually in TypeScript you use the arrow notation for functions (`=>`). In this case, however, because you need access to the `arguments` property, you have to use the regular function definition. To prevent tslint from reporting a warning about not using the arrow notation, you can explicitly disable the `no-function-expression` rule around the function definition. 1. To confirm that everything is working as expected, in the command line, execute: ```console - gulp serve --nobrowser + heft start --nobrowser ``` 1. Navigate to the hosted Workbench and add the web part to the canvas. Although visually nothing has changed, the new code base uses TypeScript and its type definitions to help you maintain the solution.