Skip to content

Commit 56bcb51

Browse files
Update DataTables tutorial: migrate from gulp to heft commands (#10608)
* Update DataTables tutorial: migrate from gulp to heft commands and fix grammar * Delete docs/images/datatables-spfx.png * Corrected image1 * Delete docs/images/datatables-spfx-list-configured.png * Corrected image2 * fix list, fix grammar --------- Co-authored-by: Andrew Connell <me@andrewconnell.com>
1 parent 716b2d2 commit 56bcb51

3 files changed

Lines changed: 60 additions & 48 deletions

File tree

6.68 KB
Loading

docs/images/datatables-spfx.png

8.76 KB
Loading

docs/spfx/web-parts/guidance/migrate-jquery-datatables-script-to-spfx.md

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
---
2-
title: Migrate jQuery and DataTables solution built using Script Editor web part to SharePoint Framework
2+
title: Migrate a jQuery and DataTables solution built using Script Editor web part to the SharePoint Framework
33
description: Migrate a SharePoint customization using DataTables to build powerful data overviews of data coming from SharePoint and external APIs.
4-
ms.date: 08/19/2020
4+
ms.date: 01/28/2026
55
ms.localizationpriority: high
66
---
77

8-
# Migrate jQuery and DataTables solution built using Script Editor web part to SharePoint Framework
8+
# Migrate a jQuery and DataTables solution built using Script Editor web part to the SharePoint Framework
99

1010
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.
1111

12-
[!INCLUDE [spfx-gulp-heft-migration-wip](../../../../includes/snippets/spfx-gulp-heft-migration-wip.md)]
13-
1412
## List of IT requests built using the Script Editor web part
1513

1614
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
9290
$(document).ready(function() {
9391
$('#requests').DataTable({
9492
'ajax': {
95-
'url': "../_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title",
93+
'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",
9694
'headers': { 'Accept': 'application/json;odata=nometadata' },
97-
'dataSrc': function(data) {
98-
return data.value.map(function(item) {
95+
'dataSrc': function(data: any) {
96+
return data.value.map(function(item: any) {
9997
return [
10098
item.ID,
101-
item.BusinessUnit,
102-
item.Category,
103-
item.Status,
104-
new Date(item.DueDate),
105-
item.AssignedTo.Title
99+
item.BusinessUnit || '',
100+
item.Category || '',
101+
item.Status || '',
102+
item.DueDate ? new Date(item.DueDate) : '',
103+
item.AssignedTo ? item.AssignedTo.Title : ''
106104
];
107105
});
108106
}
@@ -137,6 +135,19 @@ Transforming this customization to the SharePoint Framework offers a number of b
137135
138136
### Create new SharePoint Framework project
139137

138+
Before You Begin:
139+
140+
1. Create a list with columns and sample data:
141+
142+
1. Create a SharePoint list named "IT Requests"
143+
1. Add these columns with exact internal names:
144+
- BusinessUnit (Text)
145+
- Category (Text)
146+
- Status (Choice)
147+
- DueDate (Date)
148+
- AssignedTo (Person)
149+
1. Add sample data to test the solution
150+
140151
1. Start by creating a new folder for your project:
141152

142153
```console
@@ -167,7 +178,7 @@ Transforming this customization to the SharePoint Framework offers a number of b
167178

168179
### Load JavaScript libraries
169180

170-
Similar to the original solution built using the Script Editor web part, first you need to load the JavaScript libraries required by the solution. In SharePoint Framework this usually consists of two steps: specifying the URL from which the library should be loaded, and referencing the library in the code.
181+
Similar to the original solution built using the Script Editor web part, first you need to load the JavaScript libraries required by the solution. In the SharePoint Framework, this usually consists of two steps: specifying the URL from which the library should be loaded, and referencing the library in the code.
171182

172183
1. Specify the URLs from which libraries should be loaded. In the code editor, open the **./config/config.json** file, and change the `externals` section to:
173184

@@ -200,7 +211,7 @@ Similar to the original solution built using the Script Editor web part, first y
200211
> [!NOTE]
201212
> For more information on referencing external libraries in SharePoint Framework projects, see [Add an external library to your SharePoint client-side web part](../basics/add-an-external-library.md).
202213

203-
1. Open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and after the last `import` statement add:
214+
1. Open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and after the last `import` statement, add:
204215

205216
```typescript
206217
import 'jquery';
@@ -298,7 +309,7 @@ The next step is to define the Moment.js plug-in for DataTables so that dates in
298309
);
299310
```
300311

301-
1. For the web part to load the plug-in, it has to reference the newly created **moment-plugin.js** file. In the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and after the last `import` statement add:
312+
1. For the web part to load the plug-in, it has to reference the newly created **moment-plugin.js** file. In the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and after the last `import` statement, add:
302313

303314
```typescript
304315
import './moment-plugin';
@@ -314,17 +325,17 @@ The last step is to include the code that initializes the data table and loads t
314325
$(document).ready(function () {
315326
$('#requests').DataTable({
316327
'ajax': {
317-
'url': "../../_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title",
328+
'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",
318329
'headers': { 'Accept': 'application/json;odata=nometadata' },
319-
'dataSrc': function (data) {
320-
return data.value.map(function (item) {
330+
'dataSrc': function (data: any) {
331+
return data.value.map(function (item: any) {
321332
return [
322333
item.ID,
323-
item.BusinessUnit,
324-
item.Category,
325-
item.Status,
326-
new Date(item.DueDate),
327-
item.AssignedTo.Title
334+
item.BusinessUnit || '',
335+
item.Category || '',
336+
item.Status || '',
337+
item.DueDate ? new Date(item.DueDate) : '',
338+
item.AssignedTo ? item.AssignedTo.Title : ''
328339
];
329340
});
330341
}
@@ -338,7 +349,7 @@ The last step is to include the code that initializes the data table and loads t
338349
```
339350

340351
> [!NOTE]
341-
> Make sure to use internal name (or static name) of columns in `$select` and `$expend` parameters.
352+
> Make sure to use the internal name (or static name) of columns in `$select` and `$expend` parameters.
342353

343354
1. To reference this file in the web part, in the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and add `require('./script');` to the end of the `render()` method. The `render()` method should look like the following:
344355

@@ -369,10 +380,10 @@ The last step is to include the code that initializes the data table and loads t
369380
1. Verify that the web part is working as expected in the command line by executing:
370381

371382
```console
372-
gulp serve --nobrowser
383+
heft start --nobrowser
373384
```
374385

375-
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.
386+
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.
376387

377388
![IT requests displayed in a SharePoint Framework client-side web part](../../../images/datatables-spfx.png)
378389

@@ -483,17 +494,17 @@ Initially, the name of the list from which the data should be loaded was embedde
483494
$(document).ready(() => {
484495
$('table', this.domElement).DataTable({
485496
'ajax': {
486-
'url': `../../_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`,
497+
'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`,
487498
'headers': { 'Accept': 'application/json;odata=nometadata' },
488-
'dataSrc': function (data) {
489-
return data.value.map(function (item) {
499+
'dataSrc': function (data: any) {
500+
return data.value.map(function (item: any) {
490501
return [
491502
item.ID,
492-
item.BusinessUnit,
493-
item.Category,
494-
item.Status,
495-
new Date(item.DueDate),
496-
item.AssignedTo.Title
503+
item.BusinessUnit || '',
504+
item.Category || '',
505+
item.Status || '',
506+
item.DueDate ? new Date(item.DueDate) : '',
507+
item.AssignedTo ? item.AssignedTo.Title : ''
497508
];
498509
});
499510
}
@@ -510,16 +521,16 @@ Initially, the name of the list from which the data should be loaded was embedde
510521
}
511522
```
512523
513-
1. Instead of referencing the code from the **script.js** file, all of its contents are a part of the web part's `render` method. In the REST query, you can now replace the fixed name of the list with the value of the `listName` property that holds the name of the list as configured by the user. Before using the value, it's being escaped by using the lodash's `escape` function to disallow script injection.
524+
1. Instead of referencing the code from the **script.js** file, all of its contents are a part of the web part's `render` method. In the REST query, you can now replace the fixed name of the list with the value of the `listName` property that holds the name of the list as configured by the user. Before using the value, it's being escaped by using the lodash `escape` function to disallow script injection.
514525
515526
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.
516527
517-
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.
528+
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.
518529
519530
1. To verify that the web part is working as expected, run the following in the command line:
520531
521532
```console
522-
gulp serve --nobrowser
533+
heft start --nobrowser
523534
```
524535
525536
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.
@@ -530,7 +541,7 @@ Initially, the name of the list from which the data should be loaded was embedde
530541
531542
## Transform the plain JavaScript code to TypeScript
532543
533-
Using TypeScript over plain JavaScript offers a number of benefits. Not only is TypeScript easier to maintain and refactor, but it also allows you to catch errors earlier. The following steps describe how you would transform the original JavaScript code to TypeScript.
544+
Using TypeScript over plain JavaScript offers several benefits. Not only is TypeScript easier to maintain and refactor, but it also allows you to catch errors earlier. The following steps describe how you would transform the original JavaScript code to TypeScript.
534545
535546
### Add type definitions for used libraries
536547
@@ -543,7 +554,7 @@ To function properly, TypeScript requires type definitions for the different lib
543554
```
544555
545556
> [!TIP]
546-
> In this example, we are specifying the exact version of the NPM package we want to install. This will ensure that NPM installs a type declaration package that matches the version of jQuery and the datatables library we are using in our project.
557+
> In this example, we are specifying the exact version of the NPM package we want to install. This will ensure that NPM installs a type declaration package that matches the version of jQuery and the DataTables library we are using in our project.
547558
>
548559
> The `--save-dev` argument tells NPM to save the references to these two packages in the `devDependencies` collection in the **package.json** file. TypeScript declarations are only needed in development, which is why we don't want them in the `dependencies` collection.
549560
>
@@ -559,7 +570,7 @@ To function properly, TypeScript requires type definitions for the different lib
559570
560571
### Update package references
561572
562-
To use types from the installed type definitions, you've to change how you reference libraries.
573+
To use types from the installed type definitions, you have to change how you reference libraries.
563574
564575
1. In the code editor, open the **./src/webparts/itRequests/ItRequestsWebPart.ts** file, and change the `import 'jquery';` statement to:
565576
@@ -575,7 +586,7 @@ To use types from the installed type definitions, you've to change how you refer
575586
576587
### Update main web part files to TypeScript
577588
578-
Now that you've type definitions for all libraries installed in the project, you can start transforming the plain JavaScript code to TypeScript.
589+
Now that you have type definitions for all libraries installed in the project, you can start transforming the plain JavaScript code to TypeScript.
579590
580591
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:
581592
@@ -612,7 +623,7 @@ Now that you've type definitions for all libraries installed in the project, you
612623
613624
$('table', this.domElement).DataTable({
614625
'ajax': {
615-
'url': `../../_api/web/lists/getbytitle('${escape(this.properties.listName)}')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title`,
626+
'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`,
616627
'headers': { 'Accept': 'application/json;odata=nometadata' },
617628
'dataSrc': (data: { value: IRequestItem[] }): any[][] => {
618629
return data.value.map((item: IRequestItem): any[] => {
@@ -638,9 +649,9 @@ Now that you've type definitions for all libraries installed in the project, you
638649
}
639650
```
640651
641-
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.
652+
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 the 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.
642653
643-
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.
654+
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.
644655

645656
### Update the Moment.js DataTables plugin to TypeScript
646657

@@ -651,7 +662,7 @@ The last piece of the solution that needs to be transformed to TypeScript is the
651662

652663
```typescript
653664
import * as $ from 'jquery';
654-
import * as moment from 'moment';
665+
import moment from 'moment';
655666

656667
/* tslint:disable:no-function-expression */
657668
($.fn.dataTable.render as any).moment = function (from: string, to: string, locale: string): (d: any, type: string, row: any) => string {
@@ -675,11 +686,12 @@ The last piece of the solution that needs to be transformed to TypeScript is the
675686
};
676687
```
677688

678-
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.
689+
1. You start by 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.
679690
1. To confirm that everything is working as expected, in the command line, execute:
680691

681692
```console
682-
gulp serve --nobrowser
693+
heft start --nobrowser
683694
```
684695

685696
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.
697+

0 commit comments

Comments
 (0)