Adde Data Connections

This commit is contained in:
petkir 2020-06-22 22:30:37 +02:00
parent e073836425
commit 4b089918d8
5 changed files with 260 additions and 214 deletions

View File

@ -67,11 +67,14 @@ Unexpected call to method or property access.
# Webpart Steps
* DataConnection
** Task missing
* BucketEdit Does not refresh Component
# Webpart Steps Done
* PNP Placeholder Control for Config
* PNP WebpartTitle Control
* DataConnection
* Usage of BucketEdit in pane
* PNP Order pane Control
* PNP Order pane Control

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version, Guid } from '@microsoft/sp-core-library';
import { Version, Guid, Environment, EnvironmentType } from '@microsoft/sp-core-library';
import { BaseClientSideWebPart, PropertyPaneDropdown } from '@microsoft/sp-webpart-base';
import {
IPropertyPaneConfiguration,
@ -36,221 +36,219 @@ export interface IKanbanBoardWebPartProps {
export default class KanbanBoardWebPart extends BaseClientSideWebPart<IKanbanBoardWebPartProps> {
private kanbanComponent = null;
public onInit(): Promise < void> {
private statekey: string = Date.now().toString();
public onInit(): Promise<void> {
return super.onInit().then(_ => {
return super.onInit().then(_ => {
sp.setup({
spfxContext: this.context
});
sp.setup({
spfxContext: this.context
});
});
}
public render(): void {
/*
const element: React.ReactElement<IKanbanBoardProps > = React.createElement(
KanbanBoard,
{
listTitle: this.properties.listTitle,
webUrl: this.context.pageContext.web.absoluteUrl
}
);
*/
/*
const element: React.ReactElement<IMockKanbanProps > = React.createElement(
MockKanban,
{
}
);
*/
const element: React.ReactElement < IKanbanBoardV2Props > = React.createElement(
KanbanBoardV2,
{
hideWPTitle: this.properties.hideWPTitle,
title: this.properties.title,
displayMode: this.displayMode,
updateProperty: (value: string) => {
this.properties.title = value;
},
context: this.context,
listId: this.properties.listId,
configuredBuckets: this.properties.buckets
}
);
this.kanbanComponent = ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
const propertypages = [];
const generalgroups = [];
generalgroups.push(
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneToggle('hideWPTitle', {
label: 'Hide WP Title',
checked: this.properties.hideWPTitle
}),
PropertyFieldListPicker('listId', {
label: 'Select a list',
selectedList: this.properties.listId,
includeHidden: false,
orderBy: PropertyFieldListPickerOrderBy.Title,
disabled: false,
onPropertyChange: this.listConfigurationChanged.bind(this),
properties: this.properties,
context: this.context,
onGetErrorMessage: null,
deferredValidationTime: 0,
key: 'listPickerFieldId',
onListsRetrieved: (lists) => {
//TODO Check from TS Definition it should be a string but i get a number
// with Typesafe equal it fails
const alists = lists.filter((l: any) => {
return (l.BaseTemplate === 171 || l.BaseTemplate === 107);
}
);
return alists;
}
})
]
});
if (this.properties.listId && this.properties.buckets && this.properties.buckets.length > 1) {
generalgroups.push(
{
groupName: "Order Buckets",
groupFields: [
PropertyFieldOrder("orderedItems", {
key: "orderedItems",
label: "Ordered Items",
items: this.properties.buckets,
properties: this.properties,
onPropertyChange: this.onPropertyPaneFieldChanged,
onRenderItem: bucketOrder,
})
]
}
);
}
propertypages.push({
groups: generalgroups
});
if (this.properties.buckets && this.properties.buckets.length > 0) {
this.properties.buckets.forEach((b, i) => {
propertypages.push({
key: { i },
header: {
description: "Bucket Configuration"
public render(): void {
/*
const element: React.ReactElement<IKanbanBoardProps > = React.createElement(
KanbanBoard,
{
listTitle: this.properties.listTitle,
webUrl: this.context.pageContext.web.absoluteUrl
}
);
*/
/*
const element: React.ReactElement<IMockKanbanProps > = React.createElement(
MockKanban,{});
*/
console.log('bucket render webpart');
console.log(this.properties.buckets);
const element: React.ReactElement<IKanbanBoardV2Props> = React.createElement(
KanbanBoardV2,
{
hideWPTitle: this.properties.hideWPTitle,
title: this.properties.title,
displayMode: this.displayMode,
updateProperty: (value: string) => {
this.properties.title = value;
},
groups: [{
groupName: b.bucketheadline ? b.bucketheadline : b.bucket,
statekey: this.statekey,
context: this.context,
listId: this.properties.listId,
configuredBuckets: this.properties.buckets
}
);
this.kanbanComponent = ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
const propertypages = [];
const generalgroups = [];
generalgroups.push(
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneToggle('hideWPTitle', {
label: 'Hide WP Title',
checked: this.properties.hideWPTitle
}),
PropertyFieldListPicker('listId', {
label: 'Select a list',
selectedList: this.properties.listId,
includeHidden: false,
orderBy: PropertyFieldListPickerOrderBy.Title,
disabled: false,
onPropertyChange: this.listConfigurationChanged.bind(this),
properties: this.properties,
context: this.context,
onGetErrorMessage: null,
deferredValidationTime: 0,
key: 'listPickerFieldId',
onListsRetrieved: (lists) => {
//TODO Check from TS Definition it should be a string but i get a number
// with Typesafe equal it fails
if (Environment.type == EnvironmentType.Local || Environment.type == EnvironmentType.Test) {
return lists;
} else {
const alists = lists.filter((l: any) => {
return (l.BaseTemplate === 171 || l.BaseTemplate === 107);
});
return alists;
}
}
})
]
});
if (this.properties.listId && this.properties.buckets && this.properties.buckets.length > 1) {
generalgroups.push(
{
groupName: "Order Buckets",
groupFields: [
PropertyPaneBucketConfigComponent('bucket_' + i, {
key: 'bucket_' + i,
properties: cloneDeep(b),
onPropertyChange: this.bucketConfigurationChanged.bind(this)
PropertyFieldOrder("buckets", {
key: "orderedItems",
label: "Ordered Items",
items: this.properties.buckets,
properties: this.properties,
onPropertyChange: this.onPropertyPaneFieldChanged,
onRenderItem: bucketOrder,
})
]
}
]
});
);
}
propertypages.push({
groups: generalgroups
});
if (this.properties.buckets && this.properties.buckets.length > 0) {
this.properties.buckets.forEach((b, i) => {
propertypages.push({
key: { i },
header: {
description: "Bucket Configuration"
},
groups: [{
groupName: b.bucketheadline ? b.bucketheadline : b.bucket,
groupFields: [
PropertyPaneBucketConfigComponent('bucket_' + i, {
key: 'bucket_' + i,
properties: cloneDeep(b),
onPropertyChange: this.bucketConfigurationChanged.bind(this)
})
]
}
]
});
});
}
return {
pages: propertypages
};
}
return {
pages: propertypages
};
}
private listConfigurationChanged(propertyPath: string, oldValue: any, newValue: any) {
this.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
this.refreshBucket();
this.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
this.refreshBucket();
}
}
private bucketConfigurationChanged(propertyPath: string, oldValue: any, newValue: any) {
//its an array part !!!!!
if (propertyPath.indexOf('bucket_') !== -1) {
const newbuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets);
const bucketindex: number = +propertyPath.split('_')[1];
newbuckets[bucketindex] = newValue;
//merge and every else is good
//
console.log('buckes updated old saved');
console.log(oldValue);
// this.onPropertyPaneFieldChanged('buckets', this.properties.buckets, newbuckets);
this.properties.buckets = cloneDeep(newbuckets);
// array child Properties change dows not trigger rerender
// i think this is better as an Property With da DateTimeValue to force Rerender
console.log('old');
console.log(this.properties.buckets);
console.log('new');
console.log(newbuckets);
this.kanbanComponent.forceUpdate();
} else {
throw "propertypath is not a bucket";
//its an array part !!!!!
if (propertyPath.indexOf('bucket_') !== -1) {
const oribuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets);
const newbuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets);
const bucketindex: number = +propertyPath.split('_')[1];
newbuckets[bucketindex] = newValue;
//maybe better to make a array control (Update)
this.onPropertyPaneFieldChanged("buckets", oribuckets, newbuckets);
this.properties.buckets = newbuckets;
this.context.propertyPane.refresh();
this.render();
} else {
throw "propertypath is not a bucket";
}
}
}
private refreshBucket(): void {
const listId = this.properties.listId;
if(!listId || listId.length === 0) { return; }
const listId = this.properties.listId;
if (!listId || listId.length === 0) { return; }
sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get()
.then(status => {
sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get()
.then(status => {
const cols: string[] = status.Choices.map((val, index) => {
return val;
});
//matching with existing configured buckets
const currentbuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.properties.buckets, cols);
if (!currentbuckets) {
return;
}
this.properties.buckets = currentbuckets;
this.context.propertyPane.refresh();
});
const cols: string[] = status.Choices.map((val, index) => {
return val;
});
//matching with existing configured buckets
const currentbuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.properties.buckets, cols);
if (!currentbuckets) {
return;
}
this.properties.buckets = currentbuckets;
this.context.propertyPane.refresh();
});
}
protected onPropertyPaneConfigurationStart() {
// Use the list template ID to locate both the old style task lists (107) and newer task lists (171)
/*
sp.web.lists.filter("BaseTemplate eq 171 or BaseTemplate eq 107").select("Title").get().then(res => {
this.properties.lists = res.map((val, index) => {
return {
key: val.Title,
text: val.Title
};
// Use the list template ID to locate both the old style task lists (107) and newer task lists (171)
/*
sp.web.lists.filter("BaseTemplate eq 171 or BaseTemplate eq 107").select("Title").get().then(res => {
this.properties.lists = res.map((val, index) => {
return {
key: val.Title,
text: val.Title
};
});
this.context.propertyPane.refresh();
});
this.context.propertyPane.refresh();
});
*/
}
*/
}
}

View File

@ -1,14 +1,15 @@
import * as React from 'react';
import styles from './KanbanBoardV2.module.scss';
import * as strings from 'KanbanBoardWebPartStrings';
import { DisplayMode, Guid } from '@microsoft/sp-core-library';
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder";
import { WebPartContext } from '@microsoft/sp-webpart-base';
import { Spinner } from 'office-ui-fabric-react/lib/Spinner';
import { IKanbanBucket } from '../../../kanban/IKanbanBucket';
import { IKanbanTask } from '../../../kanban/IKanbanTask';
import { IKanbanTask, KanbanTaskMamagedPropertyType } from '../../../kanban/IKanbanTask';
import KanbanComponent from '../../../kanban/KanbanComponent';
import { findIndex, clone, isEqual } from '@microsoft/sp-lodash-subset';
import { findIndex, clone, isEqual, cloneDeep } from '@microsoft/sp-lodash-subset';
import { sp } from '@pnp/sp';
import { mergeBucketsWithChoices } from './helper';
@ -20,6 +21,7 @@ export interface IKanbanBoardV2Props {
context: WebPartContext;
listId: string;
configuredBuckets: IKanbanBucket[]; // need mearge with current readed
statekey: string; // force refresh ;)
}
export interface IKanbanBoardV2State {
@ -47,11 +49,12 @@ export default class KanbanBoardV2 extends React.Component<IKanbanBoardV2Props,
this._getData();
}
public shouldComponentUpdate(nextProps: IKanbanBoardV2Props, nextState: IKanbanBoardV2State): boolean {
if (!isEqual(this.state, nextState)) { return true; }
if (!isEqual(this.props, nextProps)) {
console.log('change in Props found')
return true; }
if (!isEqual(this.props, nextProps)) {
//stateKey
return true;
}
return false;
}
public componentDidUpdate(prevProps: IKanbanBoardV2Props) {
@ -59,7 +62,11 @@ export default class KanbanBoardV2 extends React.Component<IKanbanBoardV2Props,
if (this.props.listId !== prevProps.listId) {
this._getData();
}
const currentPropBuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.props.configuredBuckets, this.choices);
if (!isEqual(this.state.buckets, currentPropBuckets)) {
this.setState({ buckets: cloneDeep(currentPropBuckets) });
}
/*
const currentbuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.props.configuredBuckets, this.choices);
console.log(this.props.configuredBuckets);
console.log(currentbuckets);
@ -68,7 +75,7 @@ export default class KanbanBoardV2 extends React.Component<IKanbanBoardV2Props,
this.setState({ buckets: currentbuckets });
}
}*/
}
public render(): React.ReactElement<IKanbanBoardV2Props> {
@ -123,7 +130,15 @@ export default class KanbanBoardV2 extends React.Component<IKanbanBoardV2Props,
const elementsIndex = findIndex(this.state.tasks, element => element.taskId == taskId);
let newArray = [...this.state.tasks]; // same as Clone
newArray[elementsIndex].bucket = targetBucket.bucket;
this.setState({ tasks: newArray });
sp.web.lists.getById(this.props.listId).items.getById(+taskId).update({
Status: targetBucket.bucket
}).then(res => {
console.log("Task updated");
this.setState({ tasks: newArray });
}).catch(error=> {
this.setState({ errorMessage: 'Error Update Task Item' });
});
}
@ -146,16 +161,44 @@ export default class KanbanBoardV2 extends React.Component<IKanbanBoardV2Props,
this.setState({ isConfigured: false, loading: false, errorMessage: 'No Buckets found' });
return;
}
sp.web.lists.getById(listId).items.getAll().then(res => {
//Map Items to task
this.setState({
isConfigured: true,
loading: false,
errorMessage: undefined,
buckets: currentbuckets,
tasks: []
const odatafiels: string[] = ['AssignedTo/Id', 'AssignedTo/Title', 'AssignedTo/Name', 'AssignedTo/EMail',
'ID', 'Title', 'Status', 'Priority', 'PercentComplete', 'Body'
];
sp.web.lists.getById(listId).items
.select(odatafiels.join(','))
.expand('AssignedTo').getAll().then(res => {
const tasks: IKanbanTask[] = res.map((x) => {
return {
taskId: '' + x.ID,
title: x.Title,
htmlDescription: x.Body,
assignedTo: (x.AssignedTo && (x.AssignedTo).length === 1) ?
{
text: x.AssignedTo[0].Title
}
: undefined,
priority: x.Priority,
bucket: x.Status,
mamagedProperties: [
{
name: 'PercentComplete',
displayName: strings.PercentComplete,
type: KanbanTaskMamagedPropertyType.percent,
value: x.PercentComplete
}
]
};
});
this.setState({
isConfigured: true,
loading: false,
errorMessage: undefined,
buckets: currentbuckets,
tasks: tasks
});
});
});
});

View File

@ -2,6 +2,7 @@ define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Basic Configuration",
"ListTitleFieldLabel": "List Title"
"ListTitleFieldLabel": "List Title",
"PercentComplete": "Percent Complete"
}
});

View File

@ -2,6 +2,7 @@ declare interface IKanbanBoardWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
ListTitleFieldLabel: string;
PercentComplete: string;
}
declare module 'KanbanBoardWebPartStrings' {