Fully generic CRUD pages
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 { CrudService} from "../crud.service";
|
||||
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,18 +22,42 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
private _loading$ = new BehaviorSubject<boolean>(true);
|
||||
//private _search$ = new Subject<void>();
|
||||
@@ -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$() {
|
||||
|
||||
Reference in New Issue
Block a user