mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-09-03 01:56:16 +00:00
Performance fix: add paging for custom field select options (#10755)
This commit is contained in:
@@ -28,6 +28,16 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@if (allSelectOptions.length > SELECT_OPTION_PAGE_SIZE) {
|
||||||
|
<ngb-pagination
|
||||||
|
class="d-flex justify-content-end"
|
||||||
|
[pageSize]="SELECT_OPTION_PAGE_SIZE"
|
||||||
|
[collectionSize]="allSelectOptions.length"
|
||||||
|
[(page)]="selectOptionsPage"
|
||||||
|
[maxSize]="5"
|
||||||
|
size="sm"
|
||||||
|
></ngb-pagination>
|
||||||
|
}
|
||||||
@if (object?.id) {
|
@if (object?.id) {
|
||||||
<small class="d-block mt-2" i18n>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</small>
|
<small class="d-block mt-2" i18n>Warning: existing instances of this field will retain their current value index (e.g. option #1, #2, #3) after editing the options here</small>
|
||||||
}
|
}
|
||||||
|
@@ -125,4 +125,42 @@ describe('CustomFieldEditDialogComponent', () => {
|
|||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(document.activeElement).toBe(selectOptionInputs.last.nativeElement)
|
expect(document.activeElement).toBe(selectOptionInputs.last.nativeElement)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should send all select options including those changed in form on save', () => {
|
||||||
|
component.dialogMode = EditDialogMode.EDIT
|
||||||
|
component.object = {
|
||||||
|
id: 1,
|
||||||
|
name: 'Field 1',
|
||||||
|
data_type: CustomFieldDataType.Select,
|
||||||
|
extra_data: {
|
||||||
|
select_options: Array.from({ length: 50 }, (_, i) => ({
|
||||||
|
label: `Option ${i + 1}`,
|
||||||
|
id: `${i + 1}-xyz`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fixture.detectChanges()
|
||||||
|
component.ngOnInit()
|
||||||
|
component.selectOptionsPage = 2
|
||||||
|
fixture.detectChanges()
|
||||||
|
component.objectForm
|
||||||
|
.get('extra_data')
|
||||||
|
.get('select_options')
|
||||||
|
.get('0')
|
||||||
|
.get('label')
|
||||||
|
.setValue('Updated Option 9')
|
||||||
|
const formValues = (component as any).getFormValues()
|
||||||
|
// first item unchanged
|
||||||
|
expect(formValues.extra_data.select_options[0]).toEqual({
|
||||||
|
label: 'Option 1',
|
||||||
|
id: '1-xyz',
|
||||||
|
})
|
||||||
|
// page 2 first item updated
|
||||||
|
expect(
|
||||||
|
formValues.extra_data.select_options[component.SELECT_OPTION_PAGE_SIZE]
|
||||||
|
).toEqual({
|
||||||
|
label: 'Updated Option 9',
|
||||||
|
id: '9-xyz',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@@ -14,6 +14,7 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import {
|
import {
|
||||||
@@ -28,6 +29,8 @@ import { SelectComponent } from '../../input/select/select.component'
|
|||||||
import { TextComponent } from '../../input/text/text.component'
|
import { TextComponent } from '../../input/text/text.component'
|
||||||
import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component'
|
import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component'
|
||||||
|
|
||||||
|
const SELECT_OPTION_PAGE_SIZE = 8
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-custom-field-edit-dialog',
|
selector: 'pngx-custom-field-edit-dialog',
|
||||||
templateUrl: './custom-field-edit-dialog.component.html',
|
templateUrl: './custom-field-edit-dialog.component.html',
|
||||||
@@ -37,6 +40,7 @@ import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component'
|
|||||||
TextComponent,
|
TextComponent,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
|
NgbPaginationModule,
|
||||||
NgxBootstrapIconsModule,
|
NgxBootstrapIconsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -45,6 +49,21 @@ export class CustomFieldEditDialogComponent
|
|||||||
implements OnInit, AfterViewInit
|
implements OnInit, AfterViewInit
|
||||||
{
|
{
|
||||||
CustomFieldDataType = CustomFieldDataType
|
CustomFieldDataType = CustomFieldDataType
|
||||||
|
SELECT_OPTION_PAGE_SIZE = SELECT_OPTION_PAGE_SIZE
|
||||||
|
|
||||||
|
private _allSelectOptions: any[] = []
|
||||||
|
public get allSelectOptions(): any[] {
|
||||||
|
return this._allSelectOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
private _selectOptionsPage: number
|
||||||
|
public get selectOptionsPage(): number {
|
||||||
|
return this._selectOptionsPage
|
||||||
|
}
|
||||||
|
public set selectOptionsPage(v: number) {
|
||||||
|
this._selectOptionsPage = v
|
||||||
|
this.updateSelectOptions()
|
||||||
|
}
|
||||||
|
|
||||||
@ViewChildren('selectOption')
|
@ViewChildren('selectOption')
|
||||||
private selectOptionInputs: QueryList<ElementRef>
|
private selectOptionInputs: QueryList<ElementRef>
|
||||||
@@ -67,17 +86,10 @@ export class CustomFieldEditDialogComponent
|
|||||||
this.objectForm.get('data_type').disable()
|
this.objectForm.get('data_type').disable()
|
||||||
}
|
}
|
||||||
if (this.object?.data_type === CustomFieldDataType.Select) {
|
if (this.object?.data_type === CustomFieldDataType.Select) {
|
||||||
this.selectOptions.clear()
|
this._allSelectOptions = [
|
||||||
this.object.extra_data.select_options
|
...(this.object.extra_data.select_options ?? []),
|
||||||
.filter((option) => option)
|
]
|
||||||
.forEach((option) =>
|
this.selectOptionsPage = 1
|
||||||
this.selectOptions.push(
|
|
||||||
new FormGroup({
|
|
||||||
label: new FormControl(option.label),
|
|
||||||
id: new FormControl(option.id),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +99,19 @@ export class CustomFieldEditDialogComponent
|
|||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.selectOptionInputs.last?.nativeElement.focus()
|
this.selectOptionInputs.last?.nativeElement.focus()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.objectForm.valueChanges
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe((change) => {
|
||||||
|
// Update the relevant select options values if changed in the form, which is only a page of the entire list
|
||||||
|
this.objectForm
|
||||||
|
.get('extra_data.select_options')
|
||||||
|
?.value.forEach((option, index) => {
|
||||||
|
this._allSelectOptions[
|
||||||
|
index + (this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE
|
||||||
|
] = option
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
@@ -108,6 +133,17 @@ export class CustomFieldEditDialogComponent
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getFormValues() {
|
||||||
|
const formValues = super.getFormValues()
|
||||||
|
if (
|
||||||
|
this.objectForm.get('data_type')?.value === CustomFieldDataType.Select
|
||||||
|
) {
|
||||||
|
// Make sure we send all select options, with updated values
|
||||||
|
formValues.extra_data.select_options = this._allSelectOptions
|
||||||
|
}
|
||||||
|
return formValues
|
||||||
|
}
|
||||||
|
|
||||||
getDataTypes() {
|
getDataTypes() {
|
||||||
return DATA_TYPE_LABELS
|
return DATA_TYPE_LABELS
|
||||||
}
|
}
|
||||||
@@ -116,13 +152,35 @@ export class CustomFieldEditDialogComponent
|
|||||||
return this.dialogMode === EditDialogMode.EDIT
|
return this.dialogMode === EditDialogMode.EDIT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateSelectOptions() {
|
||||||
|
this.selectOptions.clear()
|
||||||
|
this._allSelectOptions
|
||||||
|
.slice(
|
||||||
|
(this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE,
|
||||||
|
this.selectOptionsPage * SELECT_OPTION_PAGE_SIZE
|
||||||
|
)
|
||||||
|
.forEach((option) =>
|
||||||
|
this.selectOptions.push(
|
||||||
|
new FormGroup({
|
||||||
|
label: new FormControl(option.label),
|
||||||
|
id: new FormControl(option.id),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public addSelectOption() {
|
public addSelectOption() {
|
||||||
this.selectOptions.push(
|
this._allSelectOptions.push({ label: null, id: null })
|
||||||
new FormGroup({ label: new FormControl(null), id: new FormControl(null) })
|
this.selectOptionsPage = Math.ceil(
|
||||||
|
this.allSelectOptions.length / SELECT_OPTION_PAGE_SIZE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeSelectOption(index: number) {
|
public removeSelectOption(index: number) {
|
||||||
this.selectOptions.removeAt(index)
|
this.selectOptions.removeAt(index)
|
||||||
|
this._allSelectOptions.splice(
|
||||||
|
index + (this.selectOptionsPage - 1) * SELECT_OPTION_PAGE_SIZE,
|
||||||
|
1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -147,9 +147,13 @@ export abstract class EditDialogComponent<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getFormValues(): any {
|
||||||
|
return Object.assign({}, this.objectForm.value)
|
||||||
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.error = null
|
this.error = null
|
||||||
const formValues = Object.assign({}, this.objectForm.value)
|
const formValues = this.getFormValues()
|
||||||
const permissionsObject: PermissionsFormObject =
|
const permissionsObject: PermissionsFormObject =
|
||||||
this.objectForm.get('permissions_form')?.value
|
this.objectForm.get('permissions_form')?.value
|
||||||
if (permissionsObject) {
|
if (permissionsObject) {
|
||||||
|
Reference in New Issue
Block a user