path-framework-weberm16
Version:
Path Application Framework
640 lines (600 loc) • 33.3 kB
text/typescript
import * as path from './path';
import * as autocomplete from './form/field/auto-complete/auto-complete-field.component';
import 'rxjs/add/operator/map';
import {AutoCompleteFieldEntry} from "./form/field/auto-complete/auto-complete-field-entry";
import {ValueField} from "./form/field/value-field";
import {FieldListField} from "./form/field/fieldList/field-list-field.component";
import {LabelField} from "./form/field/label/label-field.component";
import {IPageElement} from "./pathinterface";
import {RadioGroupField} from "./form/field/radio/radio-group.component";
import {Key} from "./page/element/page-element";
import {KeyUtility} from "./key-utility";
import {FormFunction} from "./form/form-function";
import {TranslationService} from "./service/translation.service";
export abstract class PathAppComponent implements path.IPathApp {
private _pageStack:path.Page[] = [];
private _formStack:path.Form[] = [];
private _userId:string;
private _texts:string[]= [];
private _version:string;
constructor(private pathService:path.PathService, private translationService:TranslationService) {
this.pathService.serverGet(this.getBackendUrl(), "/ping", (data:any) => {
let backendVersion = data["version"];
if (backendVersion != this.getFrontendVersion()) {
backendVersion = "Version mismatch: Backend (" + backendVersion + "), Frontend (" + this.getFrontendVersion() + "). Please clear cache or check server installation.";
window.alert(backendVersion);
}
this._version = backendVersion;
if (data["userId"] != null && data["userId"] != "") {
this._userId = data["userId"];
this.setCurrentPage(this.getStartPage(), null);
}
if (data["languageCode"] != null && data["languageCode"] != "") {
sessionStorage.setItem("languageCode", data["languageCode"]);
}
}, (err:any) => { console.error(err); });
this.loadApplicationTexts();
}
protected abstract getStartPage():string;
protected getApplicationLogo():string {
return null;
}
protected abstract getOwnUserForm():string;
protected abstract getGuiModel();
protected abstract getBeans();
protected abstract getHandlers();
public abstract getBackendUrl():string;
protected abstract getFrontendVersion():string;
public isLoading():boolean {
return this.pathService.isLoading();
}
private loadApplicationTexts() {
this._texts["Logout"] = this.translationService.getText("Logout");
this._texts["NotSignedIn"] = this.translationService.getText("NotSignedIn");
this._texts["SignedInAs"] = this.translationService.getText("SignedInAs");
}
public getUserId():string {
return this._userId;
}
public login(event, userId:string, password:string) {
let credentials:any = {};
credentials["username"] = userId;
credentials["password"] = password;
this.pathService.serverPost(this.getBackendUrl(), "/login", credentials, (data:any) => {
console.log("login ok, language code: " + data["languageCode"] + ", jwt:" + data["jwt"]);
sessionStorage.setItem("languageCode", data["languageCode"]);
this._userId = userId;
this.loadApplicationTexts();
this.setCurrentPage(this.getStartPage(), null); // set start page
}, (err:any) => {
this.pathService.hideLoading();
if (this.getBackendUrl().indexOf("heroku") > 0) {
alert("Login failed. Please try again after 30sec, because the Heroku backend server may be sleeping due to inactivity.")
} else {
alert("Login failed.")
}
console.error("failed login");
});
}
public logout() {
sessionStorage.clear();
this._userId == null;
console.log("logout user " + this._userId);
location.reload();
}
public showUserForm() {
this.setCurrentForm(this.getOwnUserForm(), new Key(0, "userId"), null, null); // TODO set correct key
}
public closeCurrentForm() {
this._formStack.pop();
}
public refreshCurrentPage() {
for (let element of this._pageStack[this._pageStack.length - 1].content) {
if (element instanceof path.List) {
(<path.List>element).refresh();
}
}
// breadcrumbs
if (this._pageStack[this._pageStack.length - 2] != null) {
for (let element of this._pageStack[this._pageStack.length - 2].content) {
if (element instanceof path.List) {
(<path.List>element).refresh();
}
}
}
}
public navigateBack() {
this._pageStack.pop();
this.refreshCurrentPage();
}
public navigateToPage(pageNumber:number) {
for (let k = this._pageStack.length - 1; k > pageNumber; k--) {
console.log("back");
this.navigateBack();
}
}
public yesNo(text:string, yesHandler : () => void, noHandler : () => void) {
let form:path.Form = new path.Form(this.pathService, this);
form.formFunction = new FormFunction();
form.formFunction.save = (data:any) => {
this.closeCurrentForm();
this.refreshCurrentPage();
};
form.formFunction.cancel = () => {
this.closeCurrentForm();
};
let message:path.TextField = new path.TextField(form, this.translationService);
message.type = "label";
message.visible = true;
message.labelVisible = false;
message.setValue(text);
form.fields.push(message);
let cancelButton:path.CancelButton = new path.CancelButton(form, this.translationService);
cancelButton.type = "cancelButton";
cancelButton.name = this.translationService.getText("Cancel");
cancelButton.visible = true;
form.fields.push(cancelButton);
let okButton:path.OkButton = new path.OkButton(form, this.translationService);
okButton.type = "okButton";
okButton.name = this.translationService.getText("Ok");
okButton.visible = true;
okButton.handler = {
doClick(button:path.IButton) {
yesHandler();
}
};
form.fields.push(okButton);
form.updateRows();
this._formStack.push(form);
}
public setCurrentPage(pageId:string, parentPageElement:path.PageElement) {
let page:path.Page = null;
for (var modelPage of this.getGuiModel().application.pageList) {
if (modelPage.id == pageId) {
page = new path.Page();
page.id = pageId;
page.name = this.translationService.getText(modelPage.name);
if (parentPageElement != null) {
page.name = parentPageElement.name;
}
for (var modelElement of modelPage.elementList) {
// element
let element:path.PageElement = null;
switch (modelElement.type) {
case "button":
case "newButton":
element = new path.Button(this, this.pathService, this.translationService);
element.parentPageElement = parentPageElement;
element.fromJson(modelElement);
if (modelElement["buttonhandler"] != null) {
(<path.Button>element).handler = new (this.getHandlers()[modelElement["buttonhandler"]]);
}
break;
case "deleteButton":
element = new path.PageDeleteButton(this, this.pathService, this.translationService);
element.parentPageElement = parentPageElement;
element.fromJson(modelElement);
break;
case "downloadButton": // deprecated
case "linkButton":
element = new path.LinkButton(this, this.pathService, this.translationService);
element.parentPageElement = parentPageElement;
element.fromJson(modelElement);
break;
case "backbutton":
element = new path.BackButton(this, this.pathService, this.translationService);
break;
case "inlineForm":
let inlineForm = new path.InlineForm(this, this.pathService, this.translationService);
inlineForm.fromJson(modelElement);
inlineForm.url = KeyUtility.translateUrl(modelElement["url"], inlineForm.getKey(), true, parentPageElement);
inlineForm.key = parentPageElement != null ? parentPageElement.key : null;
inlineForm.loadNextForm(true);
element = inlineForm;
break;
case "list":
let dynamicList:path.List = new path.List(this, this.pathService, this.translationService);
dynamicList.search = modelElement["search"];
// handler
if (modelElement["handler"] != null) {
dynamicList.handler = new (this.getHandlers()[modelElement["handler"]]);
}
if (modelElement["buttonhandler"] != null) {
dynamicList.buttonHandler = new (this.getHandlers()[modelElement["buttonhandler"]]);
}
dynamicList.url = KeyUtility.translateUrl(modelElement["url"], null, false, parentPageElement);
dynamicList.color = modelElement["color"];
if (modelElement["form"] != null) {
dynamicList.form = modelElement["form"]["form"];
dynamicList.formHandler = modelElement["form"]["handler"];
}
dynamicList.page = modelElement["page"];
dynamicList.icon = modelElement["icon"];
dynamicList.mockData = modelElement["data"];
dynamicList.name = this.translationService.getText(modelElement["name"]);
dynamicList.refresh();
element = dynamicList;
break;
case "ChartElement":
let chart = new path.ChartElement(this, this.pathService, this.translationService);
chart.url = KeyUtility.translateUrl(modelElement["url"], null, false, parentPageElement);
element = chart;
break;
}
if (modelElement["permissionUrl"] != null) {
element.visible = false;
let permissionUrl:string = KeyUtility.translateUrl(modelElement["permissionUrl"], null, false, parentPageElement);
let permissionHandler = (permissionElement:path.PageElement) => (data:any) => {
permissionElement.visible = data["permission"];
}
this.pathService.serverGet(this.getBackendUrl(), permissionUrl, permissionHandler(element), null);
}
element.type = modelElement.type;
element.parentPageElement = parentPageElement;
if (modelElement["width"] != null) {
element.width = modelElement["width"];
} else {
element.width = 1;
}
page.content.push(element);
}
}
}
if (page == null && pageId != null) {
this.pathService.addAlert("Missing page", pageId);
} else {
this._pageStack.push(page);
}
}
public setCurrentForm(formId:string, key:Key, handler:string, parentPageElement:path.IPageElement) {
let setCurrentForm = () => {
// build form function
let formFunction:FormFunction = new FormFunction();
formFunction.save = () => {
this.closeCurrentForm();
this.refreshCurrentPage();
};
formFunction.cancel = () => {
this.closeCurrentForm();
};
formFunction.delete = () => {
this.closeCurrentForm();
let parent:path.IPageElement = parentPageElement;
if (parent != null && parent instanceof path.PageElement && (<path.PageElement>parent).listElement) {
this.refreshCurrentPage();
} else {
this.navigateBack();
this.refreshCurrentPage();
}
};
let form:path.Form = this.createForm(formId,key,handler,formFunction, parentPageElement);
if (form != null) {
this._formStack.push(form);
}
}
// check permission
let modelForm = this.getModelForm(formId);
if (modelForm != null && modelForm["permissionUrl"] != null) {
let suffix:string = "/update";
if (key == null) {
suffix = "/create";
}
let permissionUrl:string = KeyUtility.translateUrl(modelForm["permissionUrl"] + suffix, key, false, parentPageElement);
this.pathService.serverGet(this.getBackendUrl(), permissionUrl, (data:any) => {
if (!data["permission"]) {
window.alert(this.translationService.getText("NoPermissionError"));
} else {
setCurrentForm();
}
}, null);
} else {
setCurrentForm();
}
}
private getModelForm(formId:string) {
let result = null;
for (var modelForm of this.getGuiModel().application.formList) {
if (modelForm.id === formId) {
result = modelForm;
}
}
if (result == null && formId != null) {
this.pathService.addAlert("Missing form", formId);
}
return result;
}
public createForm(formId:string, key:Key, handler:string, formFunction:FormFunction, parentPageElement:path.IPageElement):path.Form {
let form:path.Form = null;
let modelForm = this.getModelForm(formId);
if (modelForm != null) {
// create form
form = new path.Form(this.pathService, this);
form.fromJson(modelForm);
form.key = key;
form.formFunction = formFunction;
form.title = this.translationService.getText(modelForm.title);
for (var modelFormField of modelForm.formFieldList) {
// create form fields
let formField:path.FormField = null;
switch (modelFormField.type) {
case "text":
{
formField = new path.TextField(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "translation":
{
formField = new path.TranslationField(form, this.pathService, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "number":
{
formField = new path.NumberField(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "label":
{
formField = new path.LabelField(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "fieldList":
{
formField = new path.FieldListField(form, this.translationService);
formField.name = "list";
formField.fromJson(modelFormField);
if (modelFormField["url"] != null) {
let fieldListUrl:any = KeyUtility.translateUrl(modelFormField["url"], form.getKey(), false, parentPageElement);
let modelId:string = modelFormField["id"];
this.pathService.serverGet(this.getBackendUrl(), fieldListUrl, (data:any) => {
let counter:number = 1;
for (let item of data) {
let dynamicField:ValueField<any> = null;
if (item["type"] == "label") {
dynamicField = new LabelField(form, this.translationService);
} else if (item["type"] == "text") {
dynamicField = new path.TextField(form, this.translationService);
} else if (item["type"] == "translation") {
dynamicField = new path.TranslationField(form, this.pathService, this.translationService);
} else if (item["type"] == "number") {
dynamicField = new path.NumberField(form, this.translationService);
}
dynamicField.fromJson(item);
dynamicField.name = item["name"]; // do not use translation service
dynamicField.id = modelId + counter;
(<FieldListField>formField).subfields.push(dynamicField);
counter++;
}
form.updateRows();
(<FieldListField>formField).created = true;
}, null);
}
break;
}
case "date":
{
formField = new path.DateField(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "autocomplete":
{
let autoCompleteFormField = new autocomplete.AutoCompleteField(form, this.translationService, this.pathService);
autoCompleteFormField.detailForm = modelFormField["form"];
autoCompleteFormField.wordSearchEnabled = modelFormField["wordSearchEnabled"];
if (modelFormField["data"] != null) {
let data = [];
let k:number = 0;
for (let item of modelFormField["data"]) {
let entry = new AutoCompleteFieldEntry();
entry.text = item;
entry.key = k;
data.push(entry);
k++;
}
autoCompleteFormField.data = data;
autoCompleteFormField.dataLoaded = true;
}
else if (modelFormField["url"] != null) {
let autoCompleteFormFieldUrl:string = KeyUtility.translateUrl(modelFormField["url"], form.key, false, parentPageElement);
autoCompleteFormField.url = autoCompleteFormFieldUrl;
autoCompleteFormField.load();
}
else {
autoCompleteFormField.dataLoaded = true;
}
formField = autoCompleteFormField;
formField.fromJson(modelFormField);
break;
}
case "RadioGroupField":
{
let radioGroupFormField = new path.RadioGroupField(form, this.translationService);
if (modelFormField["url"] != null) {
let radiosUrl:any = KeyUtility.translateUrl(modelFormField["url"], form.getKey(), false, parentPageElement);
let radioLoader = (rgField:RadioGroupField) => (data:any) => {
for (let item of data) {
let radio = new path.Radio(form, this.translationService);
radio.name = item["name"];
radio.key = item["key"]["key"].toString(); // force radio key type string for angular2
if (radio.key == item["defaultKey"]) {
rgField.setValue(radio.key);
}
rgField.radios.push(radio);
}
rgField.created = true;
console.log("radio group field created: " + rgField.id);
}
let radioLoaderForField = radioLoader(radioGroupFormField);
this.pathService.serverGet(this.getBackendUrl(), radiosUrl, radioLoaderForField, null);
} else {
radioGroupFormField.created = true;
}
radioGroupFormField.fromJson(modelFormField);
formField = radioGroupFormField;
break;
}
case "CheckboxGroupField":
{
let checkboxGroupField = new path.CheckboxGroupField(form, this.translationService);
checkboxGroupField.fromJson(modelFormField);
formField = checkboxGroupField;
break;
}
case "ProgressBarField":
{
let progressBarField = new path.ProgressBarField(form, this.translationService);
progressBarField.fromJson(modelFormField);
formField = progressBarField;
break;
}
case "okButton":
{
formField = new path.OkButton(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "cancelButton":
{
formField = new path.CancelButton(form, this.translationService);
formField.fromJson(modelFormField);
break;
}
case "deleteButton":
{
formField = new path.FormDeleteButton(form, this.translationService);
formField.fromJson(modelFormField);
if (form.key == null) {
formField.visible = false;
}
break;
}
case "previousButton":
{
formField = new path.PreviousButton(form, this.translationService);
formField.fromJson(modelFormField);
if (form.key == null) {
formField.visible = false;
}
break;
}
default:
{
formField = new path.FormField(form, this.translationService);
formField.fromJson(modelFormField);
}
}
// Field permission (move code to FormField)
if (modelFormField["permissionUrl"] != null) {
formField.readonly = false;
let permissionUrl:string = KeyUtility.translateUrl(modelFormField["permissionUrl"], formField.getForm().getKey(), false, parentPageElement);
let permissionHandler = (permissionElement:path.FormField) => (data:any) => {
permissionElement.readonly = !data["permission"];
}
this.pathService.serverGet(formField.getForm().getApp().getBackendUrl(), permissionUrl, permissionHandler(formField), null);
}
// search parents for defaultKey
if (formField instanceof ValueField && modelFormField["defaultKey"] != null) {
let pageElement:IPageElement = parentPageElement;
while (pageElement != null) {
if (pageElement.getKey() != null && pageElement.getKey().getName() == modelFormField["defaultKey"]) {
(<ValueField<any>>formField).setValue(pageElement.getKey().getKey());
(<ValueField<any>>formField).isInitialValueSet = true;
pageElement = null;
} else {
pageElement = pageElement.getParent();
}
}
}
form.fields.push(formField);
}
form.updateRows();
// fetch data from backend
if (form.url != null && form.key != null) {
form.url = KeyUtility.translateUrl(form.url, form.getKey(), true, parentPageElement);
this.pathService.serverGet(this.getBackendUrl(), form.url, (data:any) => {
for (let field of form.fields) {
if (data[field.id] != null && field instanceof path.ValueField) {
if (field instanceof RadioGroupField) {
// TODO general solution
let setValueOfRadioGroupFieldContextWrapper = () => {
let f:RadioGroupField = <RadioGroupField>field;
let v:any = data[field.id];
//noinspection TypeScriptUnresolvedFunction
setValueOfRadioGroupField(f, v);
}
let setValueOfRadioGroupField = (radioGroupField:RadioGroupField, value:any) => {
if(!radioGroupField.created) {
console.log("Waiting for RadioGroupField " + radioGroupField.id);
console.log(radioGroupField.created);
window.setTimeout(setValueOfRadioGroupFieldContextWrapper, 50); // wait then try again
return;
}
console.log("setting radiogroupfield value");
if (value != null) {
value = value.toString(); // force radio key type string for angular2
}
radioGroupField.setValue(value);
radioGroupField.isInitialValueSet = true;
}
setValueOfRadioGroupFieldContextWrapper();
} else {
(<path.ValueField<any>>field).setValue(data[field.id]);
(<path.ValueField<any>>field).isInitialValueSet = true;
}
}
if (field instanceof FieldListField) {
let setValueOfFieldListFieldContextWrapper = () => {
let f:FieldListField = <FieldListField>field;
let d:any = data;
//noinspection TypeScriptUnresolvedFunction
setValueOfFieldListField(f, d);
}
let setValueOfFieldListField = (fieldListField:FieldListField, value:any) => {
if(!(<FieldListField>field).created) {
console.log("Waiting for FieldListField... ");
setTimeout(setValueOfFieldListFieldContextWrapper, 50); // wait then try again
return;
}
// update fields
for (let subfield of (<FieldListField>field).subfields) {
if (data[subfield.id] != null) {
subfield.setValue(data[subfield.id]);
subfield.isInitialValueSet = true;
}
}
}
setValueOfFieldListFieldContextWrapper();
}
}
}, null)
}
// execute handler
let handlerName = handler;
if (handlerName == null) {
handlerName = formId + 'Handler';
}
if (this.getBeans()[formId] != null && this.getHandlers()[handlerName] != null) {
let formBean:path.IForm = new (this.getBeans()[formId]);
let formHandler:path.IFormHandler = new (this.getHandlers()[handlerName]);
for (let a = 0; a < form.fields.length; a++) {
if (form.fields[a].id != null) {
formBean[form.fields[a].id] = form.fields[a];
}
}
form.bean = formBean;
formHandler.doLoad(form.bean);
form.handler = formHandler;
}
}
return form;
}
/*Show und Hide der Navigation (Toggle)
Quelle: https://angularfirebase.com/lessons/bootstrap-4-collapsable-navbar-work-with-angular*/
show:boolean = false;
toggleCollapse() {
this.show = !this.show}
}