Fully generic CRUD pages
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
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 {ListComponent} from "../../../common/crud/list/list.component";
|
||||||
import {CardComponent} from "../../../common/crud/card/card.component";
|
import {CardComponent} from "../../../common/crud/card/card.component";
|
||||||
import {ColorsComponent} from "../theme/colors.component";
|
import {ColorsComponent} from "../theme/colors.component";
|
||||||
|
|||||||
@@ -6,35 +6,32 @@ import { DOCUMENT } from '@angular/common';
|
|||||||
|
|
||||||
import { getStyle, rgbToHex } from '@coreui/utils/src';
|
import { getStyle, rgbToHex } from '@coreui/utils/src';
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: ''
|
|
||||||
})
|
|
||||||
export class EntitiesComponent {
|
|
||||||
|
|
||||||
constructor() {
|
export class BaseEntitiesComponent {
|
||||||
}
|
protected resource: string = "Entity";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: '<crud-list></crud-list>'
|
template: '<crud-list [resource]="this.resource"></crud-list>'
|
||||||
})
|
})
|
||||||
export class EntityListComponent {
|
export class EntityListComponent extends BaseEntitiesComponent{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: '<crud-card></crud-card>'
|
template: '<crud-card [resource]="this.resource"></crud-card>'
|
||||||
})
|
})
|
||||||
export class EntityNewComponent {
|
export class EntityNewComponent extends BaseEntitiesComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@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;
|
resource_id: string | null = null;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute,) {
|
constructor(private route: ActivatedRoute,) {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
|
|||||||
|
|
||||||
import { EntitiesRoutingModule } from './entities-routing.module';
|
import { EntitiesRoutingModule } from './entities-routing.module';
|
||||||
import { CrudModule } from '@common/crud/crud.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
|
EntitiesRoutingModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
EntitiesComponent,
|
|
||||||
EntityListComponent,
|
EntityListComponent,
|
||||||
EntityNewComponent,
|
EntityNewComponent,
|
||||||
EntityCardComponent,
|
EntityCardComponent,
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ export interface Model {
|
|||||||
styleUrls: ['./card.component.css']
|
styleUrls: ['./card.component.css']
|
||||||
})
|
})
|
||||||
export class CardComponent implements OnInit {
|
export class CardComponent implements OnInit {
|
||||||
|
@Input() resource: string | undefined;
|
||||||
@Input() resource_id: string | null = null;
|
@Input() resource_id: string | null = null;
|
||||||
form = new FormGroup({});
|
form = new FormGroup({});
|
||||||
model = {};
|
model = {};
|
||||||
fields: FormlyFieldConfig[] = [];
|
fields: FormlyFieldConfig[] = [];
|
||||||
resource: string = "Entity";
|
|
||||||
|
|
||||||
schemas = JSON.parse(`{}`);
|
schemas = JSON.parse(`{}`);
|
||||||
|
|
||||||
@@ -41,14 +41,14 @@ export class CardComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this._loading$.next(true);
|
this._loading$.next(true);
|
||||||
if (this.isCreateForm()) {
|
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)];
|
this.fields = [this.formlyJsonschema.toFieldConfig(schemas)];
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.jsonSchemasService.getUpdateResource(this.resource).subscribe((schemas: any) => {
|
this.jsonSchemasService.getUpdateResource(this.resource!).subscribe((schemas: any) => {
|
||||||
this.fields = [this.formlyJsonschema.toFieldConfig(schemas)];
|
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.model = model
|
||||||
this._loading$.next(false);
|
this._loading$.next(false);
|
||||||
});
|
});
|
||||||
@@ -58,12 +58,12 @@ export class CardComponent implements OnInit {
|
|||||||
onSubmit(model: any) {
|
onSubmit(model: any) {
|
||||||
this._loading$.next(true);
|
this._loading$.next(true);
|
||||||
if (this.isCreateForm()) {
|
if (this.isCreateForm()) {
|
||||||
this.crudService.create(model).subscribe((response: any) => {
|
this.crudService.create(this.resource!, model).subscribe((response: any) => {
|
||||||
this._loading$.next(false);
|
this._loading$.next(false);
|
||||||
this.router.navigateByUrl('/entities/' + response.id);
|
this.router.navigateByUrl(response.id);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.crudService.update(model).subscribe((model: any) => {
|
this.crudService.update(this.resource!, model).subscribe((model: any) => {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this._loading$.next(false);
|
this._loading$.next(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { FormlyBootstrapModule } from '@ngx-formly/bootstrap';
|
|||||||
import { CardComponent } from './card/card.component';
|
import { CardComponent } from './card/card.component';
|
||||||
import { ListComponent } from './list/list.component';
|
import { ListComponent } from './list/list.component';
|
||||||
import { ObjectTypeComponent } from "./object.type";
|
import { ObjectTypeComponent } from "./object.type";
|
||||||
import { CrudService } from "./crud.service";
|
import {ApiService, CrudService} from "./crud.service";
|
||||||
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
import {NgbModule} from "@ng-bootstrap/ng-bootstrap";
|
||||||
import {JsonschemasService} from "./jsonschemas.service";
|
import {JsonschemasService} from "./jsonschemas.service";
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import {JsonschemasService} from "./jsonschemas.service";
|
|||||||
ListComponent,
|
ListComponent,
|
||||||
ObjectTypeComponent
|
ObjectTypeComponent
|
||||||
],
|
],
|
||||||
providers: [ CrudService, JsonschemasService ],
|
providers: [ JsonschemasService, ApiService, CrudService ],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|||||||
@@ -1,41 +1,47 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, Inject } from '@angular/core';
|
||||||
import { Schema } from "./jsonschemas.service";
|
import { Schema } from "./jsonschemas.service";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@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 loading: boolean = false;
|
||||||
|
|
||||||
public getSchema() {
|
public getList(resource: string, page: number, size: number, sortColumn: string, sortDirection: string) {
|
||||||
return this.http.get<Schema>(`/api/v1/openapi.json`);
|
return this.http.get<{ items: [{}] }>(
|
||||||
}
|
`${this.api_root}/${resource.toLowerCase()}/?size=${size}&page=${page + 1}&sort_by=${sortDirection}(${sortColumn})`
|
||||||
|
|
||||||
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 get(id: string) {
|
public get(resource: string, id: string) {
|
||||||
return this.http.get<{}>(
|
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: [{}] }>(
|
return this.http.put<{ menu: [{}] }>(
|
||||||
`/api/v1/entity/${model._id}`,
|
`${this.api_root}/${resource.toLowerCase()}/${model._id}`,
|
||||||
model
|
model
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public create(model: any) {
|
public create(resource: string, model: any) {
|
||||||
return this.http.post<{ menu: [{}] }>(
|
return this.http.post<{ menu: [{}] }>(
|
||||||
`/api/v1/entity/`,
|
`${this.api_root}/${resource.toLowerCase()}/`,
|
||||||
model
|
model
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {CrudService} from "./crud.service";
|
import {ApiService} from "./crud.service";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
|
|
||||||
@@ -9,12 +9,12 @@ import {Injectable} from "@angular/core";
|
|||||||
export class JsonschemasService {
|
export class JsonschemasService {
|
||||||
private rawSchemas: any | null = null;
|
private rawSchemas: any | null = null;
|
||||||
|
|
||||||
constructor(private crudService: CrudService) {}
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
getSchemas(): Observable<Schema> {
|
getSchemas(): Observable<Schema> {
|
||||||
return new Observable<Schema>((observer) => {
|
return new Observable<Schema>((observer) => {
|
||||||
if (this.rawSchemas === null) {
|
if (this.rawSchemas === null) {
|
||||||
this.crudService.getSchema().subscribe((jsonSchemas: any) => {
|
this.apiService.getSchema().subscribe((jsonSchemas: any) => {
|
||||||
this.rawSchemas = jsonSchemas;
|
this.rawSchemas = jsonSchemas;
|
||||||
observer.next(this.rawSchemas)
|
observer.next(this.rawSchemas)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,18 +16,14 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">#</th>
|
<th *ngFor="let col of this.displayedColumns" scope="col" sortable="name" (sort)="onSort($event)">{{ 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>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let row of listData$ | async" (click)="onSelect(row._id)">
|
<tr *ngFor="let row of listData$ | async" (click)="onSelect(row._id)">
|
||||||
<th scope="row">{{ row.id }}</th>
|
<td *ngFor="let col of this.displayedColumns">
|
||||||
<td><ngb-highlight [result]="row.name" [term]="searchTerm"></ngb-highlight></td>
|
<ngb-highlight [result]="row[col]" [term]="searchTerm"></ngb-highlight>
|
||||||
<td><ngb-highlight [result]="row.type" [term]="searchTerm"></ngb-highlight></td>
|
</td>
|
||||||
<td><ngb-highlight [result]="row.address" [term]="searchTerm"></ngb-highlight></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -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 { BehaviorSubject } from "rxjs";
|
||||||
import { Location } from '@angular/common';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Router } from '@angular/router';
|
import { CrudService } from "../crud.service";
|
||||||
import { CrudService} from "../crud.service";
|
|
||||||
import { NgbdSortableHeader, SortColumn, SortDirection } from './sortable.directive';
|
import { NgbdSortableHeader, SortColumn, SortDirection } from './sortable.directive';
|
||||||
|
import { JsonschemasService } from "../jsonschemas.service";
|
||||||
|
|
||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
@@ -22,18 +22,42 @@ interface State {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'crud-list',
|
selector: 'crud-list',
|
||||||
templateUrl: './list.component.html',
|
templateUrl: './list.component.html',
|
||||||
styleUrls: ['./list.component.css']
|
styleUrls: ['./list.component.css'],
|
||||||
})
|
})
|
||||||
export class ListComponent {
|
export class ListComponent implements OnInit {
|
||||||
displayedColumns: string[] = ['type', 'name', 'address', '_id',];
|
@Input() resource: string = "";
|
||||||
|
@Input() columns: string[] = [];
|
||||||
|
|
||||||
|
public displayedColumns: string[] = [];
|
||||||
|
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
|
|
||||||
@ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader> = new QueryList<NgbdSortableHeader>();
|
@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();
|
this._search();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loading$ = new BehaviorSubject<boolean>(true);
|
private _loading$ = new BehaviorSubject<boolean>(true);
|
||||||
//private _search$ = new Subject<void>();
|
//private _search$ = new Subject<void>();
|
||||||
@@ -50,7 +74,7 @@ export class ListComponent {
|
|||||||
|
|
||||||
private _search() {
|
private _search() {
|
||||||
this._loading$.next(true);
|
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._listData$.next(data.items);
|
||||||
this._total$.next(data.total);
|
this._total$.next(data.total);
|
||||||
this._state.pageSize = data.size;
|
this._state.pageSize = data.size;
|
||||||
@@ -72,7 +96,7 @@ export class ListComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSelect(id: string) {
|
onSelect(id: string) {
|
||||||
this.router.navigateByUrl('/entities/' + id);
|
this.router.navigate([id], {relativeTo: this.route});
|
||||||
}
|
}
|
||||||
|
|
||||||
get listData$() {
|
get listData$() {
|
||||||
|
|||||||
Reference in New Issue
Block a user