Adding filter display and management in Crud front

This commit is contained in:
2023-03-11 22:50:14 +01:00
parent f2ddc4303e
commit ad19a50346
9 changed files with 134 additions and 1 deletions

View File

@@ -2,6 +2,7 @@
[resource]="this.resource" [resource]="this.resource"
[schema]="this.schema" [schema]="this.schema"
[columns]="this.columns" [columns]="this.columns"
[filters]="this.filters"
(result)="this.flashService.success($event)" (result)="this.flashService.success($event)"
(error)="this.flashService.error($event)"> (error)="this.flashService.error($event)">
</crud-list> </crud-list>

View File

@@ -9,6 +9,7 @@ export class BaseCrudListComponent {
@Input() resource: string = ""; @Input() resource: string = "";
@Input() columns: string[] = []; @Input() columns: string[] = [];
@Input() schema: string | undefined; @Input() schema: string | undefined;
@Input() filters: string[] = [];
constructor( constructor(
public flashService: FlashmessagesService public flashService: FlashmessagesService

View File

@@ -1,5 +1,6 @@
<base-list <base-list
[resource]="this.resource" [resource]="this.resource"
[schema]="this.schema" [schema]="this.schema"
[columns]="this.columns"> [columns]="this.columns"
[filters]="this.filters">
</base-list> </base-list>

View File

@@ -55,6 +55,8 @@ export class CrudFormlyJsonschemaOptions implements FormlyJsonschemaOptions {
field.type = "richtext"; field.type = "richtext";
} else if (schema.type == "string" && schema.format == "signature-link") { } else if (schema.type == "string" && schema.format == "signature-link") {
field.type = "signature-link"; field.type = "signature-link";
} else if (field.type == "enum" && field.props.multiple) {
field.type = 'multicheckbox';
} }
if (schema.hasOwnProperty('props')) { if (schema.hasOwnProperty('props')) {

View File

@@ -28,12 +28,14 @@ import { DictionaryService } from "./types/dictionary.service";
import { RichtextTypeComponent } from "./types/richtext.type"; import { RichtextTypeComponent } from "./types/richtext.type";
import { SignatureLinkTypeComponent } from "@common/crud/types/signature-link.type"; import { SignatureLinkTypeComponent } from "@common/crud/types/signature-link.type";
import { ClipboardModule } from '@angular/cdk/clipboard'; import { ClipboardModule } from '@angular/cdk/clipboard';
import {FilterListComponent} from "@common/crud/list/filter-list.component";
@NgModule({ @NgModule({
declarations: [ declarations: [
CardComponent, CardComponent,
ListComponent, ListComponent,
FilterListComponent,
ObjectTypeComponent, ObjectTypeComponent,
DatetimeTypeComponent, DatetimeTypeComponent,
DateTypeComponent, DateTypeComponent,

View File

@@ -206,6 +206,21 @@ export class JsonschemasService {
path.substring(pointFirstPosition + 1) path.substring(pointFirstPosition + 1)
); );
} }
get_property_by_path(resource: JSONSchema7, path: string): JSONSchema7 {
const pointFirstPosition = path.indexOf('.')
if (pointFirstPosition == -1) {
return this.get_descendant(resource, path);
}
return this.get_property_by_path(
this.get_descendant(
resource,
path.substring(0, pointFirstPosition)
),
path.substring(pointFirstPosition + 1)
);
}
} }
export interface Schema { export interface Schema {

View File

@@ -0,0 +1,88 @@
import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core";
import {JSONSchema7} from "json-schema";
import {JsonschemasService} from "@common/crud/jsonschemas.service"
import {FormGroup} from "@angular/forms";
import {FormlyFieldConfig} from "@ngx-formly/core";
import {CrudFormlyJsonschemaService} from "@common/crud/crud-formly-jsonschema.service";
@Component({
selector: 'crud-list-filter-list',
template: `
<formly-form [form]="form" [fields]="fields" [model]="this.searchTerms" (modelChange)="onModelChange($event)"></formly-form>
`,
})
export class FilterListComponent implements OnInit {
@Input() filters: string[] = [];
@Input() schema = "";
@Output() filterChange: EventEmitter<{[key: string]: any}> = new EventEmitter();
form = new FormGroup({});
fields: FormlyFieldConfig[] = [];
searchTerms = {}
public fieldJson = {
components: {},
type: "object",
properties: {},
}
constructor(private jsonSchemasService: JsonschemasService,
private formlyJsonschema: CrudFormlyJsonschemaService,) { }
ngOnInit() {
this.jsonSchemasService.getUpdateResource(this.schema!).subscribe({
next: (schema: any) => this.getFilterDefinition(schema),
error: (err) => console.log(err) /*this.error.emit("Error loading the schema:" + err)*/
});
}
getFilterDefinition(schema: JSONSchema7) {
const properties: {[key: string]: JSONSchema7} = {}
for (let filter of this.filters) {
if (this.jsonSchemasService.path_exists(schema, filter)) {
let prop = this.jsonSchemasService.get_property_by_path(schema, filter)
if (prop.hasOwnProperty('allOf')) {
// @ts-ignore
prop = schema.components.schemas[prop.allOf![0]['$ref'].replace('#/components/schemas/', '')];
prop.type = "array";
prop.items = {"type": "string", "enum": prop.enum};
}
if (prop.hasOwnProperty('readOnly') && prop.readOnly) {
prop.readOnly = false
}
properties[filter] = prop;
}
}
// @ts-ignore
this.fieldJson.components = schema.components;
this.fieldJson.properties = properties;
// @ts-ignore
this.fields = [this.formlyJsonschema.toFieldConfig(this.fieldJson)]
}
onModelChange(event: {[key: string]: any}) {
for (let p_name in event) {
// @ts-ignore
let p = this.fieldJson.properties[p_name]
if (p.type == "array" && !Array.isArray(p.items)) {
let value = []
for (let key in event[p_name]) {
if (event[p_name][key]) {
value.push(key)
}
}
if (value.length == 0 || value.length == p.items.enum.length) {
delete event[p_name];
} else {
event[p_name] = value;
}
}
}
this.filterChange.next(event);
}
}

View File

@@ -13,6 +13,9 @@
[(ngModel)]="searchTerm" [(ngModel)]="searchTerm"
/> />
</div> </div>
<div class="col-xs-3 col-sm-auto">
<crud-list-filter-list [filters]="this.filters" [schema]="this.schema!" (filterChange)="onFilterChange($event)"></crud-list-filter-list>
</div>
<span class="col col-form-label" i18n *ngIf="loading$ | async">Loading...</span> <span class="col col-form-label" i18n *ngIf="loading$ | async">Loading...</span>
</div> </div>
<div class="table-responsive-md"> <div class="table-responsive-md">

View File

@@ -13,6 +13,7 @@ interface State {
searchTerm: string; searchTerm: string;
sortColumn: SortColumn; sortColumn: SortColumn;
sortDirection: SortDirection; sortDirection: SortDirection;
searchFilters: {[key: string]: any}
} }
@Component({ @Component({
@@ -23,6 +24,7 @@ interface State {
export class ListComponent implements OnInit { export class ListComponent implements OnInit {
@Input() resource: string = ""; @Input() resource: string = "";
@Input() columns: string[] = []; @Input() columns: string[] = [];
@Input() filters: string[] = [];
@Input() schema: string | undefined; @Input() schema: string | undefined;
@Output() error: EventEmitter<string> = new EventEmitter(); @Output() error: EventEmitter<string> = new EventEmitter();
@@ -43,6 +45,7 @@ export class ListComponent implements OnInit {
searchTerm: '', searchTerm: '',
sortColumn: '_id', sortColumn: '_id',
sortDirection: 'asc', sortDirection: 'asc',
searchFilters: {}
}; };
constructor(private service: CrudService, constructor(private service: CrudService,
@@ -93,6 +96,13 @@ export class ListComponent implements OnInit {
this._loading$.next(true); this._loading$.next(true);
let sortBy = new SortBy(this.sortColumn, this.sortDirection) let sortBy = new SortBy(this.sortColumn, this.sortDirection)
let filters = this.searchTerm ? [new Filters('fulltext', 'eq', this.searchTerm)] : []; let filters = this.searchTerm ? [new Filters('fulltext', 'eq', this.searchTerm)] : [];
for (let f in this.searchFilters) {
if (Array.isArray(this.searchFilters[f])) {
filters.push(new Filters(f, 'in', this.searchFilters[f]))
} else {
filters.push(new Filters(f, 'eq', this.searchFilters[f]))
}
}
this.service.getList(this.resource, this.page, this.pageSize, [sortBy], filters).subscribe({ this.service.getList(this.resource, this.page, this.pageSize, [sortBy], filters).subscribe({
next: (data: any) => { next: (data: any) => {
@@ -107,6 +117,10 @@ export class ListComponent implements OnInit {
}); });
} }
onFilterChange(event: any) {
this.searchFilters = event;
}
onSort({ column, direction }: any) { onSort({ column, direction }: any) {
// resetting other headers // resetting other headers
this.headers.forEach((header) => { this.headers.forEach((header) => {
@@ -151,6 +165,9 @@ export class ListComponent implements OnInit {
get searchTerm() { get searchTerm() {
return this._state.searchTerm; return this._state.searchTerm;
} }
get searchFilters() {
return this._state.searchFilters;
}
set page(page: number) { set page(page: number) {
this._set({ page }); this._set({ page });
@@ -161,6 +178,9 @@ export class ListComponent implements OnInit {
set searchTerm(searchTerm: string) { set searchTerm(searchTerm: string) {
this._set({ searchTerm }); this._set({ searchTerm });
} }
set searchFilters(searchFilters: {[key: string]: any}) {
this._set({ searchFilters });
}
set sortColumn(sortColumn: SortColumn) { set sortColumn(sortColumn: SortColumn) {
this._set({ sortColumn }); this._set({ sortColumn });
} }