SharePoint CRUD sample updated to SPFx 1.6.0 (#666)

This commit is contained in:
Gautam Sheth 2018-11-09 13:14:42 +05:30 committed by Vesa Juvonen
parent 5723134c29
commit 277af705b0
32 changed files with 18514 additions and 113 deletions

View File

@ -1,8 +1,11 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.4.1",
"isCreatingSolution": false,
"environment": "spo",
"version": "1.6.0",
"libraryName": "sharepoint-crud",
"libraryId": "037cc555-b7cd-4bdc-b661-0b38a09b5844",
"environment": "spo"
"libraryId": "1409e8a2-1ce4-4888-87f8-1dc377b6b62e",
"packageManager": "npm",
"componentType": "webpart"
}
}

View File

@ -2,12 +2,12 @@
## Summary
Sample Web Parts illustrating performing SharePoint CRUD operations in React, Angular, JavaScript without any framework and using the [SP PnP JS library](https://github.com/OfficeDev/PnP-JS-Core).
Sample Web Parts illustrating performing SharePoint CRUD operations in React, Angular, JavaScript without any framework and using the [@pnp/sp library](https://github.com/pnp/pnpjs).
![Sample To do SharePoint Framework Client-Side Web Part built using Angular and ngOfficeUIFabric](./assets/preview.png)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
![drop](https://img.shields.io/badge/drop-1.6.0-green.svg)
## Applies to
@ -24,6 +24,7 @@ sharepoint-crud|Waldek Mastykarz (MVP, Rencore, @waldekm), Gautam Sheth (SharePo
Version|Date|Comments
-------|----|--------
1.3|November 1, 2018|Updated to SPFx 1.6.0
1.2|March 30, 2018|Updated to SPFx 1.4.1
1.1|March 9, 2017|Updated to SPFx GA
1.0|September 16, 2016|Initial release
@ -58,7 +59,7 @@ This sample illustrates the following concepts on top of the SharePoint Framewor
- in React
- in Angular v1.x
- without a particular JavaScript framework
- using the [SP PnP JS library](https://github.com/OfficeDev/PnP-JS-Core)
- using the [@pnp/sp library](https://github.com/PnP/PnPJS)
- using ETag to ensure data integrity when updating and deleting items
- chaining promises for performing multiple asynchronous operations as part of one use case
- breaking a chain of promises in case of an error and handling it gracefully
@ -72,8 +73,8 @@ This sample illustrates the following concepts on top of the SharePoint Framewor
- loading Angular and [ngOfficeUIFabric](http://ngofficeuifabric.com) from CDN
- using conditional rendering for one-time Web Part setup
- passing Web Part configuration to Angular and reacting to configuration changes in the Angular application
- SP PnP JS library
- using the SP PnP JS library with SharePoint Framework Client-Side Web Parts
- @pnp/sp library
- using the @pnp/sp JS library with SharePoint Framework Client-Side Web Parts
- configuring global request headers and overriding them for specific requests
- sorting and selecting top n items from a list using the fluent API

View File

@ -1,5 +1,5 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"no-framework-crud-web-part": {
@ -10,11 +10,11 @@
}
]
},
"sp-pn-p-js-crud-web-part": {
"angular-crud-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/spPnPJsCrud/SpPnPJsCrudWebPart.js",
"manifest": "./src/webparts/spPnPJsCrud/SpPnPJsCrudWebPart.manifest.json"
"entrypoint": "./lib/webparts/angularCrud/AngularCrudWebPart.js",
"manifest": "./src/webparts/angularCrud/AngularCrudWebPart.manifest.json"
}
]
},
@ -26,26 +26,26 @@
}
]
},
"angular-crud-web-part": {
"pnpjs-crud-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/angularCrud/AngularCrudWebPart.js",
"manifest": "./src/webparts/angularCrud/AngularCrudWebPart.manifest.json"
"entrypoint": "./lib/webparts/pnpjsCrud/PnpjsCrudWebPart.js",
"manifest": "./src/webparts/pnpjsCrud/PnpjsCrudWebPart.manifest.json"
}
]
}
},
"externals": {
"angular": {
"path": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js",
"path": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js",
"globalName": "angular"
},
"ng-office-ui-fabric": "https://cdnjs.cloudflare.com/ajax/libs/ngOfficeUiFabric/0.16.0/ngOfficeUiFabric.min.js"
"ng-office-ui-fabric": "https://cdnjs.cloudflare.com/ajax/libs/ngOfficeUiFabric/0.16.1/ngOfficeUiFabric.min.js"
},
"localizedResources": {
"NoFrameworkCrudWebPartStrings": "lib/webparts/noFrameworkCrud/loc/{locale}.js",
"SpPnPJsCrudWebPartStrings": "lib/webparts/spPnPJsCrud/loc/{locale}.js",
"AngularCrudWebPartStrings": "lib/webparts/angularCrud/loc/{locale}.js",
"ReactCrudWebPartStrings": "lib/webparts/reactCrud/loc/{locale}.js",
"AngularCrudWebPartStrings": "lib/webparts/angularCrud/loc/{locale}.js"
"PnpjsCrudWebPartStrings": "lib/webparts/pnpjsCrud/loc/{locale}.js"
}
}
}

View File

@ -1,3 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -1,4 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "sharepoint-crud",

View File

@ -1,8 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "sharepoint-crud-client-side-solution",
"id": "c51ba9f2-6f5c-412a-a5fc-76610f39be8c",
"version": "1.1.0.0"
"id": "1409e8a2-1ce4-4888-87f8-1dc377b6b62e",
"version": "1.0.0.0",
"includeClientSideAssets": true
},
"paths": {
"zippedPackage": "solution/sharepoint-crud.sppkg"

View File

@ -1,7 +1,8 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"

View File

@ -1,3 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

17848
samples/sharepoint-crud/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "sharepoint-crud",
"version": "1.4.1",
"version": "1.6.0",
"private": true,
"engines": {
"node": ">=0.10.0"
@ -15,27 +15,30 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "~1.4.1",
"@microsoft/sp-lodash-subset": "~1.4.1",
"@microsoft/sp-office-ui-fabric-core": "~1.4.1",
"@microsoft/sp-webpart-base": "~1.4.1",
"@pnp/common": "^1.0.3",
"@pnp/logging": "^1.0.3",
"@pnp/odata": "^1.0.3",
"@pnp/sp": "^1.0.3",
"@microsoft/sp-core-library": "1.6.0",
"@microsoft/sp-lodash-subset": "1.6.0",
"@microsoft/sp-office-ui-fabric-core": "1.6.0",
"@microsoft/sp-webpart-base": "1.6.0",
"@pnp/common": "^1.2.3",
"@pnp/logging": "^1.2.3",
"@pnp/odata": "^1.2.3",
"@pnp/sp": "^1.2.3",
"@types/angular": "^1.6.51",
"@types/es6-promise": "0.0.33",
"@types/react": "15.6.6",
"@types/react-dom": "15.5.6",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"@types/webpack-env": "1.13.1",
"react": "15.6.2",
"react-dom": "15.6.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.1",
"@microsoft/sp-module-interfaces": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.4.1",
"@microsoft/sp-build-web": "1.6.0",
"@microsoft/sp-module-interfaces": "1.6.0",
"@microsoft/sp-webpart-workbench": "1.6.0",
"tslint-microsoft-contrib": "~5.0.0",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -1,6 +1,6 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "cceebf19-128e-4ffd-bb97-9989bf7c04d4",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "74208436-1c27-40df-8cb4-1625cd764c2e",
"alias": "AngularCrudWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
@ -20,7 +20,7 @@
"default": "Angular CRUD"
},
"description": {
"default": "Sample implementation of SharePoint CRUD operations in Angular"
"default": "Sample implementation of SharePoint CRUD operations in AngularJS (1.x)"
},
"officeFabricIconFontName": "Page",
"properties": {

View File

@ -8,6 +8,9 @@
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
@ -17,38 +20,61 @@
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
:global .ms-Button {
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: #0078d7;
border-color: #0078d7;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
:global .ms-Button-label {
font-weight: 600;
font-size: 14px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
color: #ffffff;
}
&.disabled, &:disabled {

View File

@ -11,11 +11,13 @@ import * as strings from 'AngularCrudWebPartStrings';
import * as angular from 'angular';
import './app/app-module';
export interface IAngularCrudWebPartProps {
listName: string;
}
export default class AngularCrudWebPart extends BaseClientSideWebPart<IAngularCrudWebPartProps> {
private $injector: angular.auto.IInjectorService;
public render(): void {

View File

@ -1,4 +1,4 @@
define([], function () {
define([], function() {
return {
"PropertyPaneDescription": "Configure settings",
"DataGroupName": "Data",

View File

@ -1,6 +1,6 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "dd6cd5e1-46e0-46cc-a0c9-be9e9adedcad",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "b724fd00-ee58-451d-95e1-8d496e6121f6",
"alias": "NoFrameworkCrudWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json

View File

@ -8,13 +8,33 @@
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
@ -24,16 +44,17 @@
// Primary Button
min-width: 80px;
background-color: #0078d7;
border-color: #0078d7;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
@ -41,14 +62,13 @@
padding: 0 16px;
.label {
font-weight: 600;
font-size: 14px;
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
color: #ffffff;
}
&.disabled, &:disabled {
@ -61,5 +81,6 @@
color: #a6a6a6;
}
}
}
}

View File

@ -4,17 +4,22 @@ import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import { IListItem } from './IListItem';
import styles from './NoFrameworkCrudWebPart.module.scss';
import * as strings from 'NoFrameworkCrudWebPartStrings';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
export interface INoFrameworkCrudWebPartProps {
listName: string;
}
interface IListItem {
Title?: string;
Id: number;
}
export default class NoFrameworkCrudWebPart extends BaseClientSideWebPart<INoFrameworkCrudWebPartProps> {
private listItemEntityTypeName: string = undefined;
public render(): void {
@ -95,32 +100,6 @@ export default class NoFrameworkCrudWebPart extends BaseClientSideWebPart<INoFra
this.domElement.querySelector('button.delete-Button').addEventListener('click', () => { webPart.deleteItem(); });
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.DataGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
private listNotConfigured(): boolean {
return this.properties.listName === undefined ||
this.properties.listName === null ||
@ -375,4 +354,30 @@ export default class NoFrameworkCrudWebPart extends BaseClientSideWebPart<INoFra
private updateItemsHtml(items: IListItem[]): void {
this.domElement.querySelector('.items').innerHTML = items.map(item => `<li>${item.Title} (${item.Id})</li>`).join("");
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.DataGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -1,4 +1,4 @@
define([], function () {
define([], function() {
return {
"PropertyPaneDescription": "Configure settings",
"DataGroupName": "Data",

View File

@ -0,0 +1,31 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "8019d2e5-c480-4ecd-a8ac-f59ac457af96",
"alias": "PnpjsCrudWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": {
"default": "Under Development"
},
"title": {
"default": "PnP JS CRUD"
},
"description": {
"default": "Sample implementation of SharePoint CRUD operations using @pnp/sp library"
},
"officeFabricIconFontName": "Page",
"properties": {
"listName": ""
}
}
]
}

View File

@ -0,0 +1,85 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.pnpjsCrud {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
&.disabled, &:disabled {
background-color: #f4f4f4;
border-color: #f4f4f4;
cursor: default;
pointer-events: none;
.label {
color: #a6a6a6;
}
}
}
}

View File

@ -0,0 +1,288 @@
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import styles from './PnpjsCrudWebPart.module.scss';
import * as strings from 'PnpjsCrudWebPartStrings';
import { sp, Item, ItemAddResult, ItemUpdateResult } from '@pnp/sp';
export interface IPnpjsCrudWebPartProps {
listName: string;
}
interface IListItem {
Title?: string;
Id: number;
}
export default class PnpjsCrudWebPart extends BaseClientSideWebPart<IPnpjsCrudWebPartProps> {
protected onInit(): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error?: any) => void): void => {
sp.setup({
sp: {
headers: {
"Accept": "application/json; odata=nometadata"
}
}
});
resolve();
});
}
public render(): void {
this.domElement.innerHTML = `
<div class="${styles.pnpjsCrud}">
<div class="${styles.container}">
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<span class="ms-font-xl ms-fontColor-white">
Sample SharePoint CRUD operations using the SP PnP JS library
</span>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<button class="${styles.button} create-Button">
<span class="${styles.label}">Create item</span>
</button>
<button class="${styles.button} read-Button">
<span class="${styles.label}">Read item</span>
</button>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<button class="${styles.button} readall-Button">
<span class="${styles.label}">Read all items</span>
</button>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<button class="${styles.button} update-Button">
<span class="${styles.label}">Update item</span>
</button>
<button class="${styles.button} delete-Button">
<span class="${styles.label}">Delete item</span>
</button>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<div class="status"></div>
<ul class="items"><ul>
</div>
</div>
</div>
</div>
`;
this.updateStatus(this.listNotConfigured() ? 'Please configure list in Web Part properties' : 'Ready');
this.setButtonsState();
this.setButtonsEventHandlers();
}
private setButtonsState(): void {
const buttons: NodeListOf<Element> = this.domElement.querySelectorAll(`button.${styles.button}`);
const listNotConfigured: boolean = this.listNotConfigured();
for (let i: number = 0; i < buttons.length; i++) {
const button: Element = buttons.item(i);
if (listNotConfigured) {
button.setAttribute('disabled', 'disabled');
}
else {
button.removeAttribute('disabled');
}
}
}
private setButtonsEventHandlers(): void {
const webPart: PnpjsCrudWebPart = this;
this.domElement.querySelector('button.create-Button').addEventListener('click', () => { webPart.createItem(); });
this.domElement.querySelector('button.read-Button').addEventListener('click', () => { webPart.readItem(); });
this.domElement.querySelector('button.readall-Button').addEventListener('click', () => { webPart.readItems(); });
this.domElement.querySelector('button.update-Button').addEventListener('click', () => { webPart.updateItem(); });
this.domElement.querySelector('button.delete-Button').addEventListener('click', () => { webPart.deleteItem(); });
}
private listNotConfigured(): boolean {
return this.properties.listName === undefined ||
this.properties.listName === null ||
this.properties.listName.length === 0;
}
private createItem(): void {
this.updateStatus('Creating item...');
sp.web.lists.getByTitle(this.properties.listName).items.add({
'Title': `Item ${new Date()}`
}).then((result: ItemAddResult): void => {
const item: IListItem = result.data as IListItem;
this.updateStatus(`Item '${item.Title}' (ID: ${item.Id}) successfully created`);
}, (error: any): void => {
this.updateStatus('Error while creating the item: ' + error);
});
}
private readItem(): void {
this.updateStatus('Loading latest items...');
this.getLatestItemId()
.then((itemId: number): Promise<IListItem> => {
if (itemId === -1) {
throw new Error('No items found in the list');
}
this.updateStatus(`Loading information about item ID: ${itemId}...`);
return sp.web.lists.getByTitle(this.properties.listName)
.items.getById(itemId).select('Title', 'Id').get();
})
.then((item: IListItem): void => {
this.updateStatus(`Item ID: ${item.Id}, Title: ${item.Title}`);
}, (error: any): void => {
this.updateStatus('Loading latest item failed with error: ' + error);
});
}
private getLatestItemId(): Promise<number> {
return new Promise<number>((resolve: (itemId: number) => void, reject: (error: any) => void): void => {
sp.web.lists.getByTitle(this.properties.listName)
.items.orderBy('Id', false).top(1).select('Id').get()
.then((items: { Id: number }[]): void => {
if (items.length === 0) {
resolve(-1);
}
else {
resolve(items[0].Id);
}
}, (error: any): void => {
reject(error);
});
});
}
private readItems(): void {
this.updateStatus('Loading all items...');
sp.web.lists.getByTitle(this.properties.listName)
.items.select('Title', 'Id').get()
.then((items: IListItem[]): void => {
this.updateStatus(`Successfully loaded ${items.length} items`, items);
}, (error: any): void => {
this.updateStatus('Loading all items failed with error: ' + error);
});
}
private updateItem(): void {
this.updateStatus('Loading latest items...');
let latestItemId: number = undefined;
let etag: string = undefined;
this.getLatestItemId()
.then((itemId: number): Promise<Item> => {
if (itemId === -1) {
throw new Error('No items found in the list');
}
latestItemId = itemId;
this.updateStatus(`Loading information about item ID: ${itemId}...`);
return sp.web.lists.getByTitle(this.properties.listName)
.items.getById(itemId).get(undefined, {
headers: {
'Accept': 'application/json;odata=minimalmetadata'
}
});
})
.then((item: Item): Promise<IListItem> => {
etag = item["odata.etag"];
return Promise.resolve((item as any) as IListItem);
})
.then((item: IListItem): Promise<ItemUpdateResult> => {
return sp.web.lists.getByTitle(this.properties.listName)
.items.getById(item.Id).update({
'Title': `Item ${new Date()}`
}, etag);
})
.then((result: ItemUpdateResult): void => {
this.updateStatus(`Item with ID: ${latestItemId} successfully updated`);
}, (error: any): void => {
this.updateStatus('Loading latest item failed with error: ' + error);
});
}
private deleteItem(): void {
if (!window.confirm('Are you sure you want to delete the latest item?')) {
return;
}
this.updateStatus('Loading latest items...');
let latestItemId: number = undefined;
let etag: string = undefined;
this.getLatestItemId()
.then((itemId: number): Promise<Item> => {
if (itemId === -1) {
throw new Error('No items found in the list');
}
latestItemId = itemId;
this.updateStatus(`Loading information about item ID: ${latestItemId}...`);
return sp.web.lists.getByTitle(this.properties.listName)
.items.getById(latestItemId).select('Id').get(undefined, {
headers: {
'Accept': 'application/json;odata=minimalmetadata'
}
});
})
.then((item: Item): Promise<IListItem> => {
etag = item["odata.etag"];
return Promise.resolve((item as any) as IListItem);
})
.then((item: IListItem): Promise<void> => {
this.updateStatus(`Deleting item with ID: ${latestItemId}...`);
return sp.web.lists.getByTitle(this.properties.listName)
.items.getById(item.Id).delete(etag);
})
.then((): void => {
this.updateStatus(`Item with ID: ${latestItemId} successfully deleted`);
}, (error: any): void => {
this.updateStatus(`Error deleting item: ${error}`);
});
}
private updateStatus(status: string, items: IListItem[] = []): void {
this.domElement.querySelector('.status').innerHTML = status;
this.updateItemsHtml(items);
}
private updateItemsHtml(items: IListItem[]): void {
this.domElement.querySelector('.items').innerHTML = items.map(item => `<li>${item.Title} (${item.Id})</li>`).join("");
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.DataGroupName,
groupFields: [
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Configure settings",
"DataGroupName": "Data",
"ListNameFieldLabel": "List Name"
}
});

View File

@ -0,0 +1,10 @@
declare interface IPnpjsCrudWebPartStrings {
PropertyPaneDescription: string;
DataGroupName: string;
ListNameFieldLabel: string;
}
declare module 'PnpjsCrudWebPartStrings' {
const strings: IPnpjsCrudWebPartStrings;
export = strings;
}

View File

@ -1,6 +1,6 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "b1900cc3-964d-40be-95bc-633843494258",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "3d06d75b-c339-4c4c-9fd6-ea6807f6dd1b",
"alias": "ReactCrudWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json

View File

@ -30,6 +30,10 @@ export default class ReactCrudWebPart extends BaseClientSideWebPart<IReactCrudWe
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}

View File

@ -1,4 +1,4 @@
export interface IListItem {
Title?: string;
Id: number;
}
Title?: string;
Id: number;
}

View File

@ -4,4 +4,4 @@ export interface IReactCrudProps {
listName: string;
spHttpClient: SPHttpClient;
siteUrl: string;
}
}

View File

@ -8,13 +8,33 @@
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
@ -24,16 +44,17 @@
// Primary Button
min-width: 80px;
background-color: #0078d7;
border-color: #0078d7;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
@ -41,14 +62,13 @@
padding: 0 16px;
.label {
font-weight: 600;
font-size: 14px;
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
color: #ffffff;
}
&.disabled, &:disabled {
@ -61,5 +81,6 @@
color: #a6a6a6;
}
}
}
}

View File

@ -6,6 +6,7 @@ import { IListItem } from './IListItem';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
export default class ReactCrud extends React.Component<IReactCrudProps, IReactCrudState> {
private listItemEntityTypeName: string = undefined;
constructor(props: IReactCrudProps, state: IReactCrudState) {

View File

@ -2,12 +2,14 @@
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
@ -21,5 +23,12 @@
"dom",
"es2015.collection"
]
}
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -1,3 +1,32 @@
{
"rulesDirectory": "./config"
"rulesDirectory": [
"tslint-microsoft-contrib"
],
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}