src/app/crud/form/form.component.ts
selector | app-form |
styleUrls | ./form.component.scss |
templateUrl | ./form.component.html |
Properties |
Methods |
constructor(route: ActivatedRoute, querries: QueryService, router: Router, db: DbConnectionService, image: ImageService)
|
||||||||||||||||||
Defined in src/app/crud/form/form.component.ts:24
|
||||||||||||||||||
Parameters :
|
back |
back()
|
Defined in src/app/crud/form/form.component.ts:153
|
Returns :
void
|
fileSelected | ||||||
fileSelected(ev, formName)
|
||||||
Defined in src/app/crud/form/form.component.ts:160
|
||||||
Parameters :
Returns :
void
|
isNumber | ||||
isNumber(val)
|
||||
Defined in src/app/crud/form/form.component.ts:157
|
||||
Parameters :
Returns :
boolean
|
ngOnInit |
ngOnInit()
|
Defined in src/app/crud/form/form.component.ts:32
|
Returns :
void
|
onSubmit |
onSubmit()
|
Defined in src/app/crud/form/form.component.ts:122
|
Returns :
void
|
sanitizeString | ||||||
sanitizeString(s: string)
|
||||||
Defined in src/app/crud/form/form.component.ts:137
|
||||||
Parameters :
Returns :
string
|
showError | ||||||
showError(err: string)
|
||||||
Defined in src/app/crud/form/form.component.ts:116
|
||||||
Parameters :
Returns :
void
|
afterSubmit |
Default value : () => {...}
|
Defined in src/app/crud/form/form.component.ts:141
|
dataset |
Defined in src/app/crud/form/form.component.ts:24
|
error |
Type : string
|
Defined in src/app/crud/form/form.component.ts:21
|
form |
Type : UntypedFormGroup
|
Defined in src/app/crud/form/form.component.ts:16
|
inputFields |
Type : []
|
Default value : []
|
Defined in src/app/crud/form/form.component.ts:17
|
loading |
Type : boolean
|
Defined in src/app/crud/form/form.component.ts:22
|
PK |
Type : object
|
Defined in src/app/crud/form/form.component.ts:23
|
selectPicker |
Decorators :
@ViewChild('select')
|
Defined in src/app/crud/form/form.component.ts:14
|
title |
Type : string
|
Defined in src/app/crud/form/form.component.ts:20
|
type |
Type : string
|
Defined in src/app/crud/form/form.component.ts:19
|
import { Component, OnInit, ViewChild } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ImageService } from 'src/app/image.service';
import { DbConnectionService } from '../../db-connection.service';
import { QueryService } from '../query.service';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
@ViewChild('select') selectPicker;
form: UntypedFormGroup;
inputFields= []
type: string;
title: string;
error: string;
loading: boolean;
PK: object;
dataset;
constructor(private route: ActivatedRoute,
private querries: QueryService,
private router: Router,
private db: DbConnectionService,
private image: ImageService) { }
ngOnInit() {
this.route.params.subscribe((params) => {
// restore default variables
this.loading = true;
this.PK = {};
this.title = "Loading...";
this.error = "";
// invalid type
if (!params.type || !(params.type in this.querries.datasets))
return this.showError("Unkown dataset '" + params.type + "'");
this.type = params.type;
this.dataset = this.querries.datasets[params.type];
// dataset has to contain form attribute
if (!this.dataset.form)
return this.showError("Dataset '" + params.type + "' has no form attribute");
// initialize input fields
this.inputFields = Object.entries(this.dataset.form).map(([k, v])=> {
// InputField.Reference(v)
if (typeof v === "string"){
let l = [];
/** data for dropdowns is loaded asynchrone
* @var l used as reference address, will be initially empty and filled when the data is received
*/
this.db.executeQuery(v).then((r) => {
// database error
if (r["error"])
return this.showError(r["error"])
// query didn't match any data
// if (!r["data"] || r["data"].length === 0)
// return this.showError(`Didn't receive any data`)
// data didn't have 2 fields or didn't contain the primary key
//if (!(Object.keys(r["data"][0]).length === 2 && k in r["data"][0]))
//return this.showError("A reference must contain a query that returns 2 fields: PK and a string value")
// loads data using push to keep the address (reassigning would change the address)
let nameKey = Object.keys(r["data"][0]).filter(x => x !== k)[0]
r["data"].forEach(x => {
l.push({id: x[k], name: x[nameKey]})
});
})
return [k, l];
} else // InputField.Text, InputField.Number, InputField.Date, InputField.Image
return [k, v];
});
// Initialize formGroup
let fg = {}
Object.entries(this.dataset.form).forEach(([k, v]) => {
fg[k] = new UntypedFormControl('');
})
this.form = new UntypedFormGroup(fg);
// Primary key(s) are given as parameter(s) when editing an already existing entry
this.route.queryParamMap.subscribe(qMap => {
qMap = qMap['params']
if (Object.keys(qMap).length !== 0){
// dataset has to contain dataset and PK attribute
if (!(this.dataset.tableName && this.dataset.PK))
return this.showError("Dataset '" + params.type + "' has no dataset or PK attribute");
// checks if query parameters match the primary keys
if ((typeof this.dataset.PK === "object" && !(Object.keys(qMap).length === this.dataset.PK.length && this.dataset.PK.every(x => x in qMap))) ||
(typeof this.dataset.PK !== "object" && !(Object.keys(qMap).length === 1 && this.dataset.PK in qMap)))
return this.showError("Given parameter(s) do(es) not match the primary key(s)")
// fetch entry data using id
this.PK = qMap
this.querries.getEntry(qMap, this.dataset.tableName).then((r) => {
// database error
if (r["error"])
return this.showError(r["error"])
// empty response
if (!(r["data"] && r["data"][0]))
return this.showError(`No entry in ${this.dataset.tableName} found with ${Object.entries(qMap).map(([k,v]) => k + ' = "' + v + '"').join(', ')}`)
// fill out form fields
Object.entries(r["data"][0]).forEach(([k, v])=> {
if (k in this.dataset.form)
this.form.get(k).setValue(v);
})
})
this.title = `Edit entry of ${this.type}`
} else
this.title = `Create entry for ${this.type}`
this.loading = false;
})
})
}
showError(err: string){
this.title = "ERROR"
this.error = err;
this.loading = false;
}
onSubmit(){
// collects data and filters out empty fields
let d = {}
Object.entries(this.form.getRawValue()).forEach(([k, v]) => {
if (v)
d[k] = typeof v === "string" ? this.sanitizeString(v) : v;
});
// this.PK = {} when creating new user
if (Object.keys(this.PK).length === 0)
this.querries.createEntry(this.dataset.tableName, d).then(this.afterSubmit);
else
this.querries.editEntry(this.dataset.tableName, d, this.PK).then(this.afterSubmit);
}
// prevents SQL injections
sanitizeString(s: string): string{
return s.replace("\"", "");
}
afterSubmit = (r) => {
// database error
if (r['error'])
return this.showError(r['error']);
// checks if database was affected
if (r['data']['affectedRows'] === 1)
this.back();
else
this.showError("Something went wrong")
}
// go back to dataset
back(){
this.router.navigateByUrl(`/crud/dataset/${this.type}`)
}
isNumber(val): boolean { return typeof val === 'number'; }
// select file
fileSelected(ev, formName){
// no files selected
if (ev.target.files.length === 0)
return;
// reset variables
let f: File = <File>ev.target.files[0];
/// this.fileName = f.name;
// convert image to standard format
this.image.convertFileToJpegBase64(f, (c) => {
this.form.get(formName).setValue(c);
}, (err) => {
alert(`Error While loading Image\n${err}`);
}, 238, 150)
}
}
<div style="margin: 10px;">
<h1>{{title}}</h1>
<form *ngIf="!loading && !error" [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="form-group" *ngFor="let e of inputFields">
<label [for]="e[0]">{{e[0]}}</label>
<input *ngIf="isNumber(e[1]) && e[1] < 3" [type]="['text', 'number', 'date'][e[1]]" class="form-control" [id]="e[0]" [placeholder]="e[0]" [formControlName]="e[0]">
<!-- Image -->
<ng-container *ngIf="isNumber(e[1]) && e[1] === 3">
<div class="custom-file" id="inputImage">
<input
type="file"
class="custom-file-input"
id="customFile"
(change)="fileSelected($event, e[0])">
<label class="custom-file-label" for="customFile">
{{form.get(e[0]).value ? form.get(e[0]).value : 'Choose Image'}}
</label>
</div>
<!-- Display selected Image-->
<div *ngIf="form.get(e[0]).value" style="display: flex;justify-content: center;">
<img style="width: 318px;height: 200px;" [src]="form.get(e[0]).value">
</div>
<!-- Remove Image Button-->
<button
*ngIf="form.get(e[0]).value"
type="button"
class="btn btn-outline-danger btn-block"
(click)="form.get(e[0]).setValue(null)">
Remove Image
</button>
</ng-container>
<select *ngIf="!isNumber(e[1])" class="form-control" [id]="e[0]" [formControlName]="e[0]">
<option *ngFor="let x of e[1]" [value]="x.id"> {{x.name}} </option>
</select>
</div>
<div class="btn-group special" role="group" aria-label="..." style="padding:5px;width: 50%;margin-left: 25%;">
<button type="submit" class="btn btn-success btn-lg">Save</button>
<button type="button" (click)="back()" class="btn btn-danger btn-lg">Cancel</button>
</div>
</form>
<i *ngIf="error"> {{error}}</i>
</div>
./form.component.scss