Upgraded Vue SFC sample to the latest SPFx drop. Updated dependency versions. Fixed build issues.

This commit is contained in:
Sergei Sergeev 2020-04-28 00:18:08 +03:00
parent 426b91707d
commit 231f87f714
17 changed files with 9580 additions and 9614 deletions

View File

@ -19,7 +19,7 @@ Sample Todo web part demonstrating how you can utilize [Vue](https://vuejs.org/v
## Used SharePoint Framework Version
![1.4.0](https://img.shields.io/badge/drop-1.4.0-green.svg)
![1.10.0](https://img.shields.io/badge/drop-1.10.0-green.svg)
## Applies to
@ -42,6 +42,7 @@ Version|Date|Comments
0.0.4|October 7, 2017|Updated packages to latest versions, misc fixing
0.0.5|November 15, 2017|Added data provider that demonstrates the CRUD operations
0.0.6|December 11, 2018|Updated sample to SPFx 1.4 and Vue 2.5.x
0.0.7|April 28, 2020|Updated sample to SPFx 1.10 and Vue 2.6.x, fixed issues with batch loader
## Disclaimer

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": {
"to-do-bundle": {

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,8 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "vue-todo-client-side-solution",
"id": "93e1bc8f-a063-4617-aa40-837e3b39f958",
"version": "1.0.0.0"
"version": "1.0.0.0",
"isDomainIsolated": false
},
"paths": {
"zippedPackage": "solution/vue-todo.sppkg"

View File

@ -1,4 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,

View File

@ -1,46 +0,0 @@
{
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"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-case": true,
"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-unused-imports": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false,
"prefer-const": false
}
}
}

View File

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

View File

@ -3,41 +3,47 @@
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
var merge = require('webpack-merge');
const VueLoaderPlugin = require('vue-loader/lib/plugin')
build.sass.setConfig({
sassMatch: []
sassMatch: []
});
build.configureWebpack.setConfig({
additionalConfiguration: function (config) {
var vueConfig = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
module: {
rules: [
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
esModule: true
}
}]
}]
},
};
build.configureWebpack.mergeConfig({
additionalConfiguration: (generatedConfiguration) => {
return merge(config, vueConfig);
}
generatedConfiguration.plugins.push(new VueLoaderPlugin());
generatedConfiguration.resolve.alias = {
'vue$': 'vue/dist/vue.esm.js'
};
generatedConfiguration.module.rules.push({
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
esModule: true
}
}]
});
generatedConfiguration.module.rules.push({
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
});
return generatedConfiguration;
}
});
let copyOtherFiles = build.subTask('copy-other-files', function(gulp, buildOptions, done){
return gulp.src(['src/**/*.vue', 'src/**/*.scss'])
.pipe(gulp.dest(buildOptions.libFolder))
let copyOtherFiles = build.subTask('copy-other-files', function (gulp, buildOptions, done) {
return gulp.src(['src/**/*.vue', 'src/**/*.scss'])
.pipe(gulp.dest(buildOptions.libFolder))
});
build.task('copy-other-files', copyOtherFiles);
build.rig.addPostTypescriptTask(copyOtherFiles);

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +1,39 @@
{
"name": "vue-todo",
"version": "0.0.6",
"version": "0.0.7",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"main": "lib/index.js",
"dependencies": {
"@microsoft/sp-core-library": "~1.4.0",
"@microsoft/sp-lodash-subset": "~1.4.0",
"@microsoft/sp-office-ui-fabric-core": "~1.4.0",
"@microsoft/sp-webpart-base": "~1.4.0",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"vue": "^2.5.13",
"vue-class-component": "^6.1.2",
"vue-property-decorator": "^6.0.0",
"vue-template-compiler": "^2.5.13",
"minimist": ">=1.2.3",
"acorn": ">=5.7.4"
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-property-pane": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@types/es6-promise": "0.0.33",
"@types/webpack-env": "1.13.1",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^8.4.2",
"vue-template-compiler": "^2.6.11"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.0",
"@microsoft/sp-module-interfaces": "~1.4.0",
"@microsoft/sp-webpart-workbench": "~1.4.0",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2",
"css-loader": "^3.5.3",
"gulp": "~3.9.1",
"webpack-merge": "^4.1.1",
"node-sass": "~4.5.3",
"sass-loader": "~6.0.6",
"vue-loader": "^13.7.0"
"node-sass": "~4.14.0",
"sass-loader": "~8.0.2",
"tslint-microsoft-contrib": "5.0.0",
"vue-loader": "^15.9.1"
},
"scripts": {
"build": "gulp bundle",

View File

@ -1,7 +1,7 @@
import { ITaskList } from '../models/ICommonObjects';
import {
IPropertyPaneDropdownOption,
} from '@microsoft/sp-webpart-base';
} from '@microsoft/sp-property-pane';
export class Utils {

View File

@ -1,12 +1,10 @@
import {
SPHttpClient,
SPHttpClientBatch,
SPHttpClientResponse
} from '@microsoft/sp-http';
import { IWebPartContext } from '@microsoft/sp-webpart-base';
import ITodoDataProvider from '../dataProviders/ITodoDataProvider';
import { ITodoItem, ITaskList } from '../models/ICommonObjects';
import { debounce } from '@microsoft/sp-lodash-subset';
export default class SharePointDataProvider implements ITodoDataProvider {
@ -45,60 +43,13 @@ export default class SharePointDataProvider implements ITodoDataProvider {
}
public getItems(): Promise<ITodoItem[]> {
return this._getItems(this.webPartContext.spHttpClient);
}
public createItem(title: string): Promise<ITodoItem[]> {
const batch: SPHttpClientBatch = this.webPartContext.spHttpClient.beginBatch();
const batchPromises: Promise<{}>[] = [
this._createItem(batch, title),
this._getItemsBatched(batch)
];
return this._resolveBatch(batch, batchPromises);
}
public deleteItem(itemToBeDeleted: ITodoItem): Promise<ITodoItem[]> {
//Approach 1: it is not working here with DELETE
// const batch: SPHttpClientBatch = this.webPartContext.spHttpClient.beginBatch();
// const batchPromises: Promise<{}>[] = [
// this._deleteItem(batch, itemToBeDeleted),
// this._getItemsBatched(batch)
// ];
// return this._resolveBatch(batch, batchPromises);
//Approach 2:
return this._deleteItem2(this.webPartContext.spHttpClient, itemToBeDeleted);
}
public updateItem(itemUpdated: ITodoItem): Promise<ITodoItem[]> {
const batch: SPHttpClientBatch = this.webPartContext.spHttpClient.beginBatch();
const batchPromises: Promise<{}>[] = [
this._updateItem(batch, itemUpdated),
this._getItemsBatched(batch)
];
return this._resolveBatch(batch, batchPromises);
}
private _getItems(requester: SPHttpClient): Promise<ITodoItem[]> {
const queryUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}` +
`/_api/web/lists(guid'${this._selectedList.Id}')/items?$select=Id,Title,PercentComplete`;
return requester.get(queryUrl, SPHttpClient.configurations.v1)
return this.webPartContext.spHttpClient.get(queryUrl, SPHttpClient.configurations.v1)
.then((response: SPHttpClientResponse) => {
return response.json();
})
//Approach 1: Of the property names of ITodoItem are equal to the internal names of the list, this will work
// .then((json: { value: ITodoItem[] }) => {
// debugger;
// return json.value;
// // .map((task: ITodoItem) => { debugger; return task; });
// });
//Approach 2: manually create the ITodoItem object; useful when the properties are different form the internal names of the list
.then((json: any) => {
return json.value.map((item: any) => {
let newItem: ITodoItem = {
@ -111,57 +62,26 @@ export default class SharePointDataProvider implements ITodoDataProvider {
});
}
private _getItemsBatched(requester: SPHttpClientBatch): Promise<ITodoItem[]> {
const queryUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}` +
`/_api/web/lists(guid'${this._selectedList.Id}')/items?$select=Id,Title,PercentComplete`;
return requester.get(queryUrl, SPHttpClientBatch.configurations.v1)
.then((response: SPHttpClientResponse) => {
return response.json();
})
.then((json: { value: ITodoItem[] }) => {
return json.value.map((task: ITodoItem) => {
return task;
});
});
}
private _createItem(batch: SPHttpClientBatch, title: string): Promise<SPHttpClientResponse> {
public createItem(title: string): Promise<ITodoItem[]> {
const body: {} = {
'@data.type': this._selectedList.ListItemEntityTypeFullName,
'Title': title
};
return batch.post(
`${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items`,
SPHttpClientBatch.configurations.v1,
{ body: JSON.stringify(body) }
);
return this._webPartContext.spHttpClient.post(`${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items`, SPHttpClient.configurations.v1, {
body: JSON.stringify(body)
})
.then(() => {
return this.getItems();
});
}
private _deleteItem(batch: SPHttpClientBatch, item: ITodoItem): Promise<SPHttpClientResponse> {
const itemDeletedUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items(${item.Id})`;
public deleteItem(itemToBeDeleted: ITodoItem): Promise<ITodoItem[]> {
const itemDeletedUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items(${itemToBeDeleted.Id})`;
const headers: Headers = new Headers();
headers.append('If-Match', '*');
return batch.fetch(itemDeletedUrl,
SPHttpClientBatch.configurations.v1,
{
headers,
method: 'DELETE'
}
);
}
private _deleteItem2(requester: SPHttpClient, item: ITodoItem): Promise<ITodoItem[]> {
const itemDeletedUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items(${item.Id})`;
const headers: Headers = new Headers();
headers.append('If-Match', '*');
return requester.fetch(itemDeletedUrl,
return this.webPartContext.spHttpClient.fetch(itemDeletedUrl,
SPHttpClient.configurations.v1,
{
headers,
@ -174,42 +94,26 @@ export default class SharePointDataProvider implements ITodoDataProvider {
return Promise.reject(new Error(JSON.stringify(response)));
}
}).then(() => {
return this._getItems(requester);
return this.getItems();
});
}
private _updateItem(batch: SPHttpClientBatch, item: ITodoItem): Promise<SPHttpClientResponse> {
const itemUpdatedUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items(${item.Id})`;
public updateItem(itemUpdated: ITodoItem): Promise<ITodoItem[]> {
const itemUpdatedUrl: string = `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists(guid'${this._selectedList.Id}')/items(${itemUpdated.Id})`;
const headers: Headers = new Headers();
headers.append('If-Match', '*');
const body: {} = {
'@data.type': this._selectedList.ListItemEntityTypeFullName,
'PercentComplete': item.PercentComplete
'PercentComplete': itemUpdated.PercentComplete
};
return batch.fetch(itemUpdatedUrl,
SPHttpClientBatch.configurations.v1,
{
body: JSON.stringify(body),
headers,
method: 'PATCH'
}
);
}
private _resolveBatch(batch: SPHttpClientBatch, promises: Promise<{}>[]): Promise<ITodoItem[]> {
return batch.execute()
.then(() => {
return Promise.all(promises);
}).then((values: any) => {
return Promise.resolve(values[values.length - 1]);
// return values[values.length - 1];
}).catch((ex) => {
throw ex;
});
return this._webPartContext.spHttpClient.fetch(itemUpdatedUrl, SPHttpClient.configurations.v1, {
body: JSON.stringify(body),
headers,
method: 'PATCH'
}).then(() => {
return this.getItems();
});
}
}

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,11 +1,11 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "a0e1eddd-ea67-4775-a52b-0141c5807146",
"alias": "ToDoWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "a0e1eddd-ea67-4775-a52b-0141c5807146",

View File

@ -1,15 +1,15 @@
import { Version, Environment, EnvironmentType } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneDropdown,
IPropertyPaneField,
PropertyPaneLabel,
IPropertyPaneDropdownOption
BaseClientSideWebPart
} from '@microsoft/sp-webpart-base';
import { findIndex } from '@microsoft/sp-lodash-subset';
import {
IPropertyPaneConfiguration,
IPropertyPaneDropdownOption,
PropertyPaneDropdown
} from "@microsoft/sp-property-pane";
import Vue from 'vue';
import TodoComponent from './components/todo/Todo.vue';
import { ITodoProps } from './components/todo/ITodoProps';

View File

@ -1,11 +1,23 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
@ -15,5 +27,12 @@
"dom",
"es2015.collection"
]
}
},
"include": [
"src/**/*.ts", "src/webparts/themed/index.tsx"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -1,3 +1,30 @@
{
"rulesDirectory": "./config"
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"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
}
}