In the article Develop First Client Side Web Part, we developed the basic SharePoint client web part which can run independently without any interaction with SharePoint.
In this article, we will explore how to interact with the SharePoint list for CRUD (Create, Read, Update, and Delete) operations using Knockout JS. Knockout JS is not natively supported by SharePoint Framework.
Brief info about Knockout JS
KnockoutJS was developed and maintained as an open source project by Steve Sanderson, a Microsoft employee. Knockout JS follows JavaScript implementation of the Model-View-ViewModel pattern with templates. Read more about KnockoutJS here.
Create SPFx Solution
Open the command prompt. Create a directory for SPFx solution.
Configure Property for List Name
SPFx solutions by default have the description property created. Let us change the property to list name. We will use this property to configure the list name on which the CRUD operation is to perform.
KnockoutJS was developed and maintained as an open source project by Steve Sanderson, a Microsoft employee. Knockout JS follows JavaScript implementation of the Model-View-ViewModel pattern with templates. Read more about KnockoutJS here.
Create SPFx Solution
Open the command prompt. Create a directory for SPFx solution.
- md spfx-crud-knockoutjs
Navigate to the above-created directory.
- cd spfx-crud-knockout js
Run Yeoman SharePoint Generator to create the solution.
- yo @microsoft/sharepoint
Yeoman generator will present you with the wizard by asking questions about the solution to be created.
Solution Name
Hit Enter to have a default name (spfx-crud-knockoutjs in this case) or type in any other name for your solution.
Selected choice - Hit Enter.
Hit Enter to have a default name (spfx-crud-knockoutjs in this case) or type in any other name for your solution.
Selected choice - Hit Enter.
Target for component
Here, we can select the target environment where we are planning to deploy the client webpart; i.e., SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Here, we can select the target environment where we are planning to deploy the client webpart; i.e., SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
Location of files
We may choose to use the same folder or create a subfolder for our solution.
We may choose to use the same folder or create a subfolder for our solution.
Selected choice - Same folder
Deployment option
Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selected choice - N (install on each site explicitly)
Type of client-side component to create
We can choose to create client side webpart or an extension. Choose webpart option.
We can choose to create client side webpart or an extension. Choose webpart option.
Selected choice - WebPart
Web part name
Hit enter to select the default name or type in any other name.
Hit enter to select the default name or type in any other name.
Selected choice - KnockoutCRUD
Web part description
Hit enter to select the default description or type in any other value.
Hit enter to select the default description or type in any other value.
Selected choice - CRUD operations with Knockout JS
Framework to use
Select any JavaScript framework to develop the component. Available choices are (No JavaScript Framework, React, and Knockout)
Select any JavaScript framework to develop the component. Available choices are (No JavaScript Framework, React, and Knockout)
Selected choice - Knockout
Yeoman generator will perform a scaffolding process to generate the solution. The scaffolding process will take a significant amount of time. Once the scaffolding process is completed, lock down the version of project dependencies by running the below command
Yeoman generator will perform a scaffolding process to generate the solution. The scaffolding process will take a significant amount of time. Once the scaffolding process is completed, lock down the version of project dependencies by running the below command
- npm shrinkwrap
In the command prompt type the below command to open the solution in the code editor of your choice.
- code .
SPFx solutions by default have the description property created. Let us change the property to list name. We will use this property to configure the list name on which the CRUD operation is to perform.
Step 1
Open mystrings.d.ts under \src\webparts\knockoutCrud\loc\ folder
Step 2
Rename DescriptionFieldLabel to ListNameFieldLabel
Open mystrings.d.ts under \src\webparts\knockoutCrud\loc\ folder
Step 2
Rename DescriptionFieldLabel to ListNameFieldLabel
- declare interface IKnockoutCrudWebPartStrings {
- PropertyPaneDescription: string;
- BasicGroupName: string;
- ListNameFieldLabel: string;
- }
- declare module 'KnockoutCrudWebPartStrings' {
- const strings: IKnockoutCrudWebPartStrings;
- export = strings;
- }
Step 3
In en-us.js file under \src\webparts\knockoutCrud\loc\ folder set the display name for listName property
In en-us.js file under \src\webparts\knockoutCrud\loc\ folder set the display name for listName property
- define([], function() {
- return {
- "PropertyPaneDescription": "Description",
- "BasicGroupName": "Group Name",
- "ListNameFieldLabel": "List Name"
- }
- });
Step 4
Open main webpart file (KnockoutCrudWebPart.ts) under \src\webparts\knockoutCrud folder.
Step 5
Rename description property pane field to listName
Open main webpart file (KnockoutCrudWebPart.ts) under \src\webparts\knockoutCrud folder.
Step 5
Rename description property pane field to listName
- import * as ko from 'knockout';
- import { Version } from '@microsoft/sp-core-library';
- import {
- BaseClientSideWebPart,
- IPropertyPaneConfiguration,
- PropertyPaneTextField
- } from '@microsoft/sp-webpart-base';
- import * as strings from 'KnockoutCrudWebPartStrings';
- import KnockoutCrudViewModel, { IKnockoutCrudBindingContext } from './KnockoutCrudViewModel';
- let _instance: number = 0;
- export interface IKnockoutCrudWebPartProps {
- listName: string;
- }
- export default class KnockoutCrudWebPart extends BaseClientSideWebPart<IKnockoutCrudWebPartProps> {
- private _id: number;
- private _componentElement: HTMLElement;
- private _koDescription: KnockoutObservable<string> = ko.observable('');
- /**
- * Shouter is used to communicate between web part and view model.
- */
- private _shouter: KnockoutSubscribable<{}> = new ko.subscribable();
- /**
- * Initialize the web part.
- */
- protected onInit(): Promise<void> {
- this._id = _instance++;
- const tagName: string = `ComponentElement-${this._id}`;
- this._componentElement = this._createComponentElement(tagName);
- this._registerComponent(tagName);
- // When web part description is changed, notify view model to update.
- this._koDescription.subscribe((newValue: string) => {
- this._shouter.notifySubscribers(newValue, 'description');
- });
- const bindings: IKnockoutCrudBindingContext = {
- listName: this.properties.listName,
- shouter: this._shouter
- };
- ko.applyBindings(bindings, this._componentElement);
- return super.onInit();
- }
- public render(): void {
- if (!this.renderedOnce) {
- this.domElement.appendChild(this._componentElement);
- }
- this._koDescription(this.properties.listName);
- }
- private _createComponentElement(tagName: string): HTMLElement {
- const componentElement: HTMLElement = document.createElement('div');
- componentElement.setAttribute('data-bind', `component: { name: "${tagName}", params: $data }`);
- return componentElement;
- }
- private _registerComponent(tagName: string): void {
- ko.components.register(
- tagName,
- {
- viewModel: KnockoutCrudViewModel,
- template: require('./KnockoutCrud.template.html'),
- synchronous: false
- }
- );
- }
- protected get dataVersion(): Version {
- return Version.parse('1.0');
- }
- protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
- return {
- pages: [
- {
- header: {
- description: strings.PropertyPaneDescription
- },
- groups: [
- {
- groupName: strings.BasicGroupName,
- groupFields: [
- PropertyPaneTextField('listName', {
- label: strings.ListNameFieldLabel
- })
- ]
- }
- ]
- }
- ]
- };
- }
- }
Step 6
Update the ViewModel inside KnockoutCrudViewModel.ts to reflect listName property
Update the ViewModel inside KnockoutCrudViewModel.ts to reflect listName property
- import * as ko from 'knockout';
- import styles from './KnockoutCrud.module.scss';
- import { IKnockoutCrudWebPartProps } from './KnockoutCrudWebPart';
- export interface IKnockoutCrudBindingContext extends IKnockoutCrudWebPartProps {
- shouter: KnockoutSubscribable<{}>;
- }
- export default class KnockoutCrudViewModel {
- public listName: KnockoutObservable<string> = ko.observable('');
- public knockoutCrudClass: string = styles.knockoutCrud;
- public containerClass: string = styles.container;
- public rowClass: string = styles.row;
- public columnClass: string = styles.column;
- public titleClass: string = styles.title;
- public subTitleClass: string = styles.subTitle;
- public descriptionClass: string = styles.description;
- public buttonClass: string = styles.button;
- public labelClass: string = styles.label;
- constructor(bindings: IKnockoutCrudBindingContext) {
- this.listName(bindings.listName);
- // When web part description is updated, change this view model's description.
- bindings.shouter.subscribe((value: string) => {
- this.listName(value);
- }, this, 'listName');
- }
- }
In the template (KnockoutCrud.template.html) reflects the listName property
- <div data-bind="attr: { class:knockoutCrudClass }">
- <div data-bind="attr: { class:containerClass }">
- <div data-bind="attr: { class:rowClass }">
- <div data-bind="attr: { class:columnClass }">
- <span data-bind="attr: { class:titleClass }">Welcome to SharePoint!</span>
- <p data-bind="attr: { class:subTitleClass }">Customize SharePoint experiences using Web Parts.</p>
- <p data-bind="attr: { class:descriptionClass }, text:listName"></p>
- <a href="https://aka.ms/spfx" data-bind="attr: { class:buttonClass }">
- <span data-bind="attr: { class:labelClass }">Learn more</span>
- </a>
- </div>
- </div>
- </div>
- </div>
Step 8
In the command prompt, type “gulp serve”
Step 9
In the SharePoint local workbench page, add the web part.
Step 10
Edit the web part to ensure the listName property pane field is getting reflected.
In the command prompt, type “gulp serve”
Step 9
In the SharePoint local workbench page, add the web part.
Step 10
Edit the web part to ensure the listName property pane field is getting reflected.
Configure ViewModel
Step 1
Open KnockoutCrudViewModel.ts, and add the below import statements

Join the conversation! Your thoughts help the community grow.