Füge Filter für die angezeigte Kategorie hinzu und aktualisiere die WebPart-Einstellungen

This commit is contained in:
Torsten Brendgen
2026-08-23 22:51:28 +02:00
parent bf9781d043
commit 6cc80b7d72
9 changed files with 103 additions and 16 deletions
+39 -8
View File
@@ -36,7 +36,8 @@ export class AccordionDataService {
contentField: string,
categoryField: string,
sortField: string,
sortAscending: boolean
sortAscending: boolean,
categoryValue: string
): Promise<IAccordionItem[]> {
if (!this.isGuid(listId)) { return Promise.reject(new Error('Invalid list ID.')); }
const fields: string[] = this.uniqueFields(['Id', titleField, contentField, categoryField]);
@@ -48,12 +49,36 @@ export class AccordionDataService {
'?$select=' + fields.join(',') + '&$orderby=' + orderField + (sortAscending ? ' asc' : ' desc') + '&$top=5000';
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
.then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response)) // tslint:disable-line:no-any
.then((payload: any): IAccordionItem[] => this.results(payload).map((item: any): IAccordionItem => ({ // tslint:disable-line:no-any
id: Number(item.Id),
title: this.toText(item[titleField]),
content: this.toText(item[contentField]),
category: categoryField ? this.toText(item[categoryField]) : ''
})));
.then((payload: any): IAccordionItem[] => this.results(payload) // tslint:disable-line:no-any
.filter((item: any): boolean => !categoryValue || // tslint:disable-line:no-any
this.toValues(item[categoryField]).indexOf(categoryValue) >= 0)
.map((item: any): IAccordionItem => ({ // tslint:disable-line:no-any
id: Number(item.Id),
title: this.toText(item[titleField]),
content: this.toText(item[contentField]),
category: categoryField ? this.toText(item[categoryField]) : ''
})));
}
public getCategoryValues(listId: string, categoryField: string): Promise<string[]> {
if (!this.isGuid(listId) || !this.isFieldName(categoryField)) { return Promise.resolve([]); }
const url: string = this.webUrl + "/_api/web/lists(guid'" + this.cleanGuid(listId) + "')/items" +
'?$select=' + categoryField + '&$top=5000';
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
.then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response)) // tslint:disable-line:no-any
.then((payload: any): string[] => { // tslint:disable-line:no-any
const values: string[] = [];
const keys: { [key: string]: boolean } = {};
const items: any[] = this.results(payload); // tslint:disable-line:no-any
for (let i: number = 0; i < items.length; i++) {
const current: string[] = this.toValues(items[i][categoryField]);
for (let valueIndex: number = 0; valueIndex < current.length; valueIndex++) {
const key: string = current[valueIndex].toLocaleLowerCase();
if (!keys[key]) { keys[key] = true; values.push(current[valueIndex]); }
}
}
return values.sort((left: string, right: string): number => left.localeCompare(right));
});
}
private get webUrl(): string { return this.context.pageContext.web.absoluteUrl.replace(/\/$/, ''); }
@@ -88,6 +113,13 @@ export class AccordionDataService {
return '';
}
private toValues(value: any): string[] { // tslint:disable-line:no-any
if (value === undefined || value === null) { return []; }
const source: any[] = value && Array.isArray(value.results) ? value.results : [value]; // tslint:disable-line:no-any
return source.map((entry: any): string => this.toText(entry).trim()) // tslint:disable-line:no-any
.filter((entry: string): boolean => !!entry);
}
private isSupportedField(type: string): boolean {
return ['Text', 'Note', 'Choice', 'MultiChoice', 'Number', 'Integer', 'Boolean', 'DateTime', 'Computed'].indexOf(String(type)) >= 0;
}
@@ -95,4 +127,3 @@ export class AccordionDataService {
private isGuid(value: string): boolean { return /^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(this.cleanGuid(value)); }
private cleanGuid(value: string): string { return String(value || '').replace(/[{}]/g, '').toLowerCase(); }
}
@@ -20,6 +20,7 @@
"titleField": "Title",
"contentField": "",
"categoryField": "",
"selectedCategory": "",
"sortField": "Title",
"sortAscending": true,
"allowMultiple": false,
+48 -2
View File
@@ -25,6 +25,7 @@ export interface IAccordionWebPartProps {
titleField: string;
contentField: string;
categoryField: string;
selectedCategory: string;
sortField: string;
sortAscending: boolean;
allowMultiple: boolean;
@@ -36,15 +37,19 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
private service: AccordionDataService;
private lists: IAccordionListOption[] = [];
private fields: IAccordionFieldOption[] = [];
private categories: string[] = [];
private loadingLists: boolean = false;
private loadingFields: boolean = false;
private loadingCategories: boolean = false;
private requestVersion: number = 0;
private disposed: boolean = false;
protected onInit(): Promise<void> {
this.service = new AccordionDataService(this.context);
return super.onInit().then((): Promise<void> => this.loadLists())
.then((): Promise<void> => this.properties.listId ? this.loadFields(this.properties.listId) : Promise.resolve());
.then((): Promise<void> => this.properties.listId ? this.loadFields(this.properties.listId) : Promise.resolve())
.then((): Promise<void> => this.properties.listId && this.properties.categoryField
? this.loadCategories(this.properties.listId, this.properties.categoryField) : Promise.resolve());
}
public render(): void {
@@ -60,7 +65,8 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
this.properties.contentField,
this.properties.categoryField || '',
this.properties.sortField || this.properties.titleField,
this.properties.sortAscending !== false
this.properties.sortAscending !== false,
this.properties.selectedCategory || ''
).then((items: IAccordionItem[]): void => {
if (this.disposed || version !== this.requestVersion) { return; }
if (!items.length) { this.renderStatus(strings.EmptyMessage, false); return; }
@@ -94,6 +100,9 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
if (this.properties.listId && !this.fields.length && !this.loadingFields) {
this.loadFields(this.properties.listId);
}
if (this.properties.listId && this.properties.categoryField && !this.categories.length && !this.loadingCategories) {
this.loadCategories(this.properties.listId, this.properties.categoryField);
}
}
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { // tslint:disable-line:no-any
@@ -102,10 +111,17 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
this.properties.titleField = '';
this.properties.contentField = '';
this.properties.categoryField = '';
this.properties.selectedCategory = '';
this.properties.sortField = '';
this.fields = [];
this.categories = [];
this.context.propertyPane.refresh();
this.loadFields(String(newValue || '')).then((): void => this.render());
} else if (propertyPath === 'categoryField' && newValue !== oldValue) {
this.properties.selectedCategory = '';
this.categories = [];
this.context.propertyPane.refresh();
this.loadCategories(this.properties.listId, String(newValue || '')).then((): void => this.render());
}
}
@@ -114,8 +130,12 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
key: field.internalName, text: field.title + ' (' + field.internalName + ')'
}));
const categoryOptions: IPropertyPaneDropdownOption[] = [{ key: '', text: strings.NoCategoryOption }];
const categoryValueOptions: IPropertyPaneDropdownOption[] = [{ key: '', text: strings.AllCategoriesOption }];
const listOptions: IPropertyPaneDropdownOption[] = [{ key: '', text: strings.SelectOption }];
for (let i: number = 0; i < fieldOptions.length; i++) { categoryOptions.push(fieldOptions[i]); }
for (let i: number = 0; i < this.categories.length; i++) {
categoryValueOptions.push({ key: this.categories[i], text: this.categories[i] });
}
for (let i: number = 0; i < this.lists.length; i++) {
listOptions.push({ key: this.lists[i].id, text: this.lists[i].title });
}
@@ -142,6 +162,10 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
label: strings.CategoryFieldLabel, options: categoryOptions,
disabled: !this.properties.listId || this.loadingFields
}),
PropertyPaneDropdown('selectedCategory', {
label: strings.CategoryFilterLabel, options: categoryValueOptions,
disabled: !this.properties.categoryField || this.loadingCategories
}),
PropertyPaneDropdown('sortField', {
label: strings.SortFieldLabel, options: fieldOptions,
disabled: !this.properties.listId || this.loadingFields
@@ -200,6 +224,28 @@ export default class AccordionWebPart extends BaseClientSideWebPart<IAccordionWe
});
}
private loadCategories(listId: string, categoryField: string): Promise<void> {
if (!listId || !categoryField) {
this.categories = [];
this.loadingCategories = false;
this.refreshPropertyPane();
return Promise.resolve();
}
this.loadingCategories = true;
this.refreshPropertyPane();
return this.service.getCategoryValues(listId, categoryField).then((categories: string[]): void => {
this.categories = categories;
if (categories.indexOf(this.properties.selectedCategory) < 0) { this.properties.selectedCategory = ''; }
}).catch((error: Error): void => {
this.categories = [];
this.properties.selectedCategory = '';
if (window.console) { console.error('[Accordion] Categories could not be loaded.', error); }
}).then((): void => {
this.loadingCategories = false;
this.refreshPropertyPane();
});
}
private applyFieldDefaults(): void {
const names: string[] = this.fields.map((field: IAccordionFieldOption): string => field.internalName);
if (names.indexOf(this.properties.titleField) < 0) {
+2 -1
View File
@@ -7,6 +7,7 @@ define([], function() {
"TitleFieldLabel": "Spalte für Überschrift",
"ContentFieldLabel": "Spalte für Inhalt",
"CategoryFieldLabel": "Spalte für Kategorie (optional)",
"CategoryFilterLabel": "Angezeigte Kategorie",
"SortFieldLabel": "Sortierspalte",
"SortAscendingLabel": "Aufsteigend sortieren",
"AllowMultipleLabel": "Mehrere geöffnete Bereiche erlauben",
@@ -17,6 +18,7 @@ define([], function() {
"ShowCategoriesLabel": "Kategorieüberschriften anzeigen",
"SelectOption": "Auswählen...",
"NoCategoryOption": "Keine Kategorien",
"AllCategoriesOption": "Alle Kategorien",
"ConfigureMessage": "Konfigurieren Sie in den WebPart-Einstellungen eine Liste sowie Überschrift- und Inhaltsspalte.",
"LoadingMessage": "Akkordeon wird geladen...",
"EmptyMessage": "Es wurden keine Einträge gefunden.",
@@ -26,4 +28,3 @@ define([], function() {
"DefaultCategory": "Weitere"
};
});
+2 -1
View File
@@ -7,6 +7,7 @@ define([], function() {
"TitleFieldLabel": "Heading column",
"ContentFieldLabel": "Content column",
"CategoryFieldLabel": "Category column (optional)",
"CategoryFilterLabel": "Displayed category",
"SortFieldLabel": "Sort column",
"SortAscendingLabel": "Sort ascending",
"AllowMultipleLabel": "Allow multiple open sections",
@@ -17,6 +18,7 @@ define([], function() {
"ShowCategoriesLabel": "Show category headings",
"SelectOption": "Select...",
"NoCategoryOption": "No categories",
"AllCategoriesOption": "All categories",
"ConfigureMessage": "Configure a list, heading column and content column in the web part settings.",
"LoadingMessage": "Loading accordion...",
"EmptyMessage": "No entries were found.",
@@ -26,4 +28,3 @@ define([], function() {
"DefaultCategory": "Other"
};
});
+2
View File
@@ -6,6 +6,7 @@ declare interface IAccordionWebPartStrings {
TitleFieldLabel: string;
ContentFieldLabel: string;
CategoryFieldLabel: string;
CategoryFilterLabel: string;
SortFieldLabel: string;
SortAscendingLabel: string;
AllowMultipleLabel: string;
@@ -16,6 +17,7 @@ declare interface IAccordionWebPartStrings {
ShowCategoriesLabel: string;
SelectOption: string;
NoCategoryOption: string;
AllCategoriesOption: string;
ConfigureMessage: string;
LoadingMessage: string;
EmptyMessage: string;