Fully generic CRUD pages

This commit is contained in:
2023-01-18 19:43:13 +01:00
parent 15f101eb9f
commit ddb3706e44
9 changed files with 84 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {EntitiesComponent, EntityCardComponent, EntityListComponent, EntityNewComponent} from "./entities.component";
import { EntityCardComponent, EntityListComponent, EntityNewComponent } from "./entities.component";
import {ListComponent} from "../../../common/crud/list/list.component";
import {CardComponent} from "../../../common/crud/card/card.component";
import {ColorsComponent} from "../theme/colors.component";

View File

@@ -6,35 +6,32 @@ import { DOCUMENT } from '@angular/common';
import { getStyle, rgbToHex } from '@coreui/utils/src';
@Component({
template: ''
})
export class EntitiesComponent {
constructor() {
}
export class BaseEntitiesComponent {
protected resource: string = "Entity";
}
@Component({
template: '<crud-list></crud-list>'
template: '<crud-list [resource]="this.resource"></crud-list>'
})
export class EntityListComponent {
export class EntityListComponent extends BaseEntitiesComponent{
}
@Component({
template: '<crud-card></crud-card>'
template: '<crud-card [resource]="this.resource"></crud-card>'
})
export class EntityNewComponent {
export class EntityNewComponent extends BaseEntitiesComponent {
}
@Component({
template: '<crud-card [resource_id]="this.resource_id"></crud-card>'
template: '<crud-card [resource]="this.resource" [resource_id]="this.resource_id"></crud-card>'
})
export class EntityCardComponent implements OnInit {
export class EntityCardComponent extends BaseEntitiesComponent implements OnInit {
resource_id: string | null = null;
constructor(private route: ActivatedRoute,) {
super();
}
ngOnInit(): void {

View File

@@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { EntitiesRoutingModule } from './entities-routing.module';
import { CrudModule } from '@common/crud/crud.module'
import {EntitiesComponent, EntityCardComponent, EntityListComponent, EntityNewComponent} from "./entities.component";
import { EntityCardComponent, EntityListComponent, EntityNewComponent} from "./entities.component";
@@ -14,7 +14,6 @@ import {EntitiesComponent, EntityCardComponent, EntityListComponent, EntityNewCo
EntitiesRoutingModule
],
declarations: [
EntitiesComponent,
EntityListComponent,
EntityNewComponent,
EntityCardComponent,

View File

@@ -18,11 +18,11 @@ export interface Model {
styleUrls: ['./card.component.css']
})
export class CardComponent implements OnInit {
@Input() resource: string | undefined;
@Input() resource_id: string | null = null;
form = new FormGroup({});
model = {};
fields: FormlyFieldConfig[] = [];
resource: string = "Entity";
schemas = JSON.parse(`{}`);
@@ -41,14 +41,14 @@ export class CardComponent implements OnInit {
ngOnInit(): void {
this._loading$.next(true);
if (this.isCreateForm()) {
this.jsonSchemasService.getCreateResource(this.resource).subscribe((schemas: any) => {
this.jsonSchemasService.getCreateResource(this.resource!).subscribe((schemas: any) => {
this.fields = [this.formlyJsonschema.toFieldConfig(schemas)];
})
} else {
this.jsonSchemasService.getUpdateResource(this.resource).subscribe((schemas: any) => {
this.jsonSchemasService.getUpdateResource(this.resource!).subscribe((schemas: any) => {
this.fields = [this.formlyJsonschema.toFieldConfig(schemas)];
})
this.crudService.get(this.resource_id!).subscribe((model: any) => {
this.crudService.get(this.resource!, this.resource_id!).subscribe((model: any) => {
this.model = model
this._loading$.next(false);
});
@@ -58,12 +58,12 @@ export class CardComponent implements OnInit {
onSubmit(model: any) {
this._loading$.next(true);
if (this.isCreateForm()) {
this.crudService.create(model).subscribe((response: any) => {
this.crudService.create(this.resource!, model).subscribe((response: any) => {
this._loading$.next(false);
this.router.navigateByUrl('/entities/' + response.id);
this.router.navigateByUrl(response.id);
});
} else {
this.crudService.update(model).subscribe((model: any) => {
this.crudService.update(this.resource!, model).subscribe((model: any) => {
this.model = model;
this._loading$.next(false);
});

View File

@@ -12,7 +12,7 @@ import { FormlyBootstrapModule } from '@ngx-formly/bootstrap';
import { CardComponent } from './card/card.component';
import { ListComponent } from './list/list.component';
import { ObjectTypeComponent } from "./object.type";
import { CrudService } from "./crud.service";
import {ApiService, CrudService} from "./crud.service";
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
import {JsonschemasService} from "./jsonschemas.service";
@@ -23,7 +23,7 @@ import {JsonschemasService} from "./jsonschemas.service";
ListComponent,
ObjectTypeComponent
],
providers: [ CrudService, JsonschemasService ],
providers: [ JsonschemasService, ApiService, CrudService ],
imports: [
CommonModule,
HttpClientModule,

View File

@@ -1,41 +1,47 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, Inject } from '@angular/core';
import { Schema } from "./jsonschemas.service";
@Injectable()
export class CrudService {
export class ApiService {
constructor(protected http: HttpClient) {}
constructor(private http: HttpClient) {}
protected api_root: string = '/api/v1'
public getSchema() {
return this.http.get<Schema>(`${this.api_root}/openapi.json`);
}
}
@Injectable()
export class CrudService extends ApiService {
public loading: boolean = false;
public getSchema() {
return this.http.get<Schema>(`/api/v1/openapi.json`);
}
public getList(page: number, size: number, sortColumn: string, sortDirection: string) {
return this.http.get<{ menu: [{}] }>(
`/api/v1/entity/?size=${size}&page=${page + 1}&sort_by=${sortDirection}(${sortColumn})`
public getList(resource: string, page: number, size: number, sortColumn: string, sortDirection: string) {
return this.http.get<{ items: [{}] }>(
`${this.api_root}/${resource.toLowerCase()}/?size=${size}&page=${page + 1}&sort_by=${sortDirection}(${sortColumn})`
);
}
public get(id: string) {
public get(resource: string, id: string) {
return this.http.get<{}>(
`/api/v1/entity/${id}`
`${this.api_root}/${resource.toLowerCase()}/${id}`
);
}
public update(model: any) {
public update(resource: string, model: any) {
return this.http.put<{ menu: [{}] }>(
`/api/v1/entity/${model._id}`,
`${this.api_root}/${resource.toLowerCase()}/${model._id}`,
model
);
}
public create(model: any) {
public create(resource: string, model: any) {
return this.http.post<{ menu: [{}] }>(
`/api/v1/entity/`,
`${this.api_root}/${resource.toLowerCase()}/`,
model
);
}

View File

@@ -1,4 +1,4 @@
import {CrudService} from "./crud.service";
import {ApiService} from "./crud.service";
import {Observable} from "rxjs";
import {Injectable} from "@angular/core";
@@ -9,12 +9,12 @@ import {Injectable} from "@angular/core";
export class JsonschemasService {
private rawSchemas: any | null = null;
constructor(private crudService: CrudService) {}
constructor(private apiService: ApiService) {}
getSchemas(): Observable<Schema> {
return new Observable<Schema>((observer) => {
if (this.rawSchemas === null) {
this.crudService.getSchema().subscribe((jsonSchemas: any) => {
this.apiService.getSchema().subscribe((jsonSchemas: any) => {
this.rawSchemas = jsonSchemas;
observer.next(this.rawSchemas)
});

View File

@@ -16,18 +16,14 @@
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col" sortable="name" (sort)="onSort($event)">Name</th>
<th scope="col" sortable="type" (sort)="onSort($event)">Type</th>
<th scope="col" sortable="address" (sort)="onSort($event)">Address</th>
<th *ngFor="let col of this.displayedColumns" scope="col" sortable="name" (sort)="onSort($event)">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of listData$ | async" (click)="onSelect(row._id)">
<th scope="row">{{ row.id }}</th>
<td><ngb-highlight [result]="row.name" [term]="searchTerm"></ngb-highlight></td>
<td><ngb-highlight [result]="row.type" [term]="searchTerm"></ngb-highlight></td>
<td><ngb-highlight [result]="row.address" [term]="searchTerm"></ngb-highlight></td>
<td *ngFor="let col of this.displayedColumns">
<ngb-highlight [result]="row[col]" [term]="searchTerm"></ngb-highlight>
</td>
</tr>
</tbody>
</table>

View File

@@ -1,9 +1,9 @@
import { Component, ViewChildren, QueryList } from '@angular/core';
import { Component, ViewChildren, QueryList, Input, OnInit } from '@angular/core';
import { BehaviorSubject } from "rxjs";
import { Location } from '@angular/common';
import { Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { CrudService } from "../crud.service";
import { NgbdSortableHeader, SortColumn, SortDirection } from './sortable.directive';
import { JsonschemasService } from "../jsonschemas.service";
interface SearchResult {
@@ -22,16 +22,40 @@ interface State {
@Component({
selector: 'crud-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
styleUrls: ['./list.component.css'],
})
export class ListComponent {
displayedColumns: string[] = ['type', 'name', 'address', '_id',];
export class ListComponent implements OnInit {
@Input() resource: string = "";
@Input() columns: string[] = [];
public displayedColumns: string[] = [];
loading: boolean = false;
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader> = new QueryList<NgbdSortableHeader>();
constructor(private location: Location, public service: CrudService, private router: Router) {
constructor(private service: CrudService,
private jsonSchemasService: JsonschemasService,
private router: Router,
private route: ActivatedRoute,
) { }
ngOnInit(): void {
this.jsonSchemasService.getUpdateResource(this.resource!).subscribe((schemas: any) => {
if (this.columns.length == 0) {
for (let param_name in schemas.properties) {
if (param_name != "_id") {
this.displayedColumns.push(param_name);
}
}
} else {
for (let column in this.columns) {
if (column in schemas.properties) {
this.displayedColumns.push(column);
}
}
}
})
this._search();
}
@@ -50,7 +74,7 @@ export class ListComponent {
private _search() {
this._loading$.next(true);
this.service.getList(this.page - 1, this.pageSize, this.sortColumn, this.sortDirection).subscribe((data: any) => {
this.service.getList(this.resource, this.page - 1, this.pageSize, this.sortColumn, this.sortDirection).subscribe((data: any) => {
this._listData$.next(data.items);
this._total$.next(data.total);
this._state.pageSize = data.size;
@@ -72,7 +96,7 @@ export class ListComponent {
}
onSelect(id: string) {
this.router.navigateByUrl('/entities/' + id);
this.router.navigate([id], {relativeTo: this.route});
}
get listData$() {