svogv
Version:
A decorator based approach for model driven forms, including an advanced DataGrid and a TreeView component.
1,159 lines (1,135 loc) • 120 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/forms'), require('@angular/common'), require('@angular/router')) :
typeof define === 'function' && define.amd ? define('svogv', ['exports', '@angular/core', '@angular/forms', '@angular/common', '@angular/router'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.svogv = {}, global.ng.core, global.ng.forms, global.ng.common, global.ng.router));
}(this, (function (exports, core, forms, common, router) { 'use strict';
/**
* This decorator is for validation of mandatory fields.
* The default message is 'The field {keyName} is required'.
*
* @param msg The error message shown in case of error. A default value is being provided if omitted.
*
*/
function Required(msg) {
function requiredInternalSetup(target, key) {
Object.defineProperty(target, "__isRequired__" + key, {
get: function () { return true; },
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errRequired__" + key, {
value: msg || "The field " + key + " is required",
enumerable: false,
configurable: false
});
}
// the original decorator
function requiredInternal(target, property) {
requiredInternalSetup(target, property.toString());
}
// return the decorator
return requiredInternal;
}
/**
* The maxlength decorator assures that a string field contains not more than a number of characters.
*
* @param len: the maximum length.
* @param msg: A custom message.
*
*/
function MaxLength(len, msg) {
function maxLengthInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasMaxLength__" + key, {
value: len,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errMaxLength__" + key, {
value: msg || "The field " + key + " has max length of " + len + " characters",
enumerable: false,
configurable: false
});
}
// the original decorator
function maxLengthInternal(target, property) {
maxLengthInternalSetup(target, property.toString());
}
// return the decorator
return maxLengthInternal;
}
/**
* The minlength decorator assures that a string field contains at least a number of characters.
*
* @param len: the required length.
* @param msg: A custom message.
*
*/
function MinLength(len, msg) {
function minLengthInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasMinLength__" + key, {
value: len,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errMinLength__" + key, {
value: msg || "The field " + key + " needs at least " + len + " characters",
enumerable: false,
configurable: false
});
}
// the original decorator
function minLengthInternal(target, property) {
minLengthInternalSetup(target, property.toString());
}
// return the decorator
return minLengthInternal;
}
/**
* The decorator that assures that a string field contains at least a number of characters and a minimum number, too.
* The default message is 'The field {fieldname} needs at least {minlength} characters'.
*
* @param min: The required length.
* @param max: The maximum length.
* @param msg: Optionally a custom message.
*
*/
function StringLength(min, max, msg) {
function stringLengthInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasMaxLength__" + key, {
value: max,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errMaxLength__" + key, {
value: msg || "The field " + key + " has max length of " + max + " characters",
enumerable: false,
configurable: false
});
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasMinLength__" + key, {
value: min,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errMinLength__" + key, {
value: msg || "The field " + key + " needs at least " + min + " characters",
enumerable: false,
configurable: false
});
}
// the original decorator
function stringLengthInternal(target, property) {
stringLengthInternalSetup(target, property.toString());
}
// return the decorator
return stringLengthInternal;
}
/**
* The decorator assures that a string field fullfilles a regular expression pattern.
*
* @param pattern: The expression as RegExp.
* @param msg: A custom message.
*
*/
function Pattern(pattern, msg) {
function patternInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasPattern__" + key, {
value: true,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errPattern__" + key, {
value: msg || "The field " + key + " must fullfill the pattern " + pattern,
enumerable: false,
configurable: false
});
}
// the original decorator
function patternInternal(target, property) {
patternInternalSetup(target, property.toString());
}
// return the decorator
return patternInternal;
}
/**
* Validates a field against an email pattern.
* Based on "pattern", so in form one must use `hasError('pattern')` to get validation results.
*
* @param msg A custom message. If not provided "The field ffff must contain a valid e-mail address."
* will be generated, while ffff is the property name.
*
*/
function Email(msg) {
function emailInternalSetup(target, key) {
// tslint:disable-next-line:max-line-length
var pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasPattern__" + key, {
value: pattern,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errPattern__" + key, {
value: msg || "The field " + key + " must contain a valid e-mail address.",
enumerable: false,
configurable: false
});
}
// the original decorator
function emailInternal(target, property) {
emailInternalSetup(target, property.toString());
}
// return the decorator
return emailInternal;
}
/**
* Validates a field against an range. Applies to numerical values or dates.
*
* The range's values are included in the valid range.
*
* @param from The minimum value (included) as number or Date
* @param to The maximum value (included) as number or Date
* @param msg A custom message. If not provided "The field [field] does not fall into the range from [from] to [to]"
* will be generated, while [field] is the propertie's name.
*/
function Range(from, to, msg) {
function rangeInternalSetup(target, key) {
// property value
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasRangeFrom__" + key, {
value: from,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__hasRangeTo__" + key, {
value: to,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errRange__" + key, {
value: msg
|| "The field " + key + " does not fall into the range from " + from + " to " + to,
enumerable: false,
configurable: false
});
}
// the original decorator
function rangeInternal(target, property) {
rangeInternalSetup(target, property.toString());
}
// return the decorator
return rangeInternal;
}
/**
* The compare decorator compares two field's values and
* shows an error message on the decorated field. The other field (compared to) does
* not has a decorator nor receives a message.
*
* @param withProperty A string that represents the compared field's name.
* @param msg A custom message.
*
*/
function Compare(withProperty, msg) {
function compareInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__hasCompareProperty__" + key, {
value: true,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__withCompare__" + key, {
value: withProperty,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__errCompareProperty__" + key, {
value: msg
|| "The field " + key + " must have the same value as field " + withProperty,
enumerable: false,
configurable: false
});
}
// the original decorator
function compareInternal(target, property) {
compareInternalSetup(target, property.toString());
}
// return the decorator
return compareInternal;
}
var displayName = 'displayName';
var displayOrder = 'displayOrder';
var displayDesc = 'displayDesc';
/**
* The Display decorator.
*
* This decorator can be used on fields. It's being used to create label in {@link EditorComponent} and
* headers in the {@link DataGridComponent}. Additional parameters are provided to refine forms further.
*
* @param name The Name or Label that appears in forms or as header in grids.
* @param order If one uses `AutoFormComponent` to create a whole form from a model, this controls the element's order.
* @param description A tooltip, which can be used optionally.
*/
function Display(name, order, description) {
if (order === void 0) { order = 0; }
function displayInternalSetup(target, key) {
order = parseInt(order.toString(), 10);
// create a helper property to transport a meta data value
Object.defineProperty(target, "__" + displayName + "__" + key, {
value: name,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__" + displayOrder + "__" + key, {
value: order,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__" + displayDesc + "__" + key, {
value: description,
enumerable: false,
configurable: false
});
}
// the original decorator
function displayInternal(target, property) {
displayInternalSetup(target, property.toString());
}
// return the decorator
return displayInternal;
}
/**
* Internal access to the provided meta data value for the name property.
*/
Display.Name = function (target, key, def) { return target["__" + displayName + "__" + key] || def; };
/**
* Internal access to the provided meta data value for the order property.
*/
Display.Order = function (target, key, def) { return target["__" + displayOrder + "__" + key] || def; };
/**
* Internal access to the provided meta data value for the description property.
*/
Display.Desc = function (target, key, def) { return target["__" + displayDesc + "__" + key] || def; };
var uiHint = 'uiHint';
/**
* The UiHint decorator.
* Currently it can contain any set of style rules that apply to the <th> element that forms the grid's table header cells.
* The application makes use of the [ngStyle] directive. The object's structure must be made in a way [ngStyle] can handle it.
*
* @param hide The style definition.
*/
function UiHint(uiHintRule) {
function uiHintInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__" + uiHint + "__" + key, {
value: uiHintRule,
enumerable: false,
configurable: false
});
}
// the original decorator
function uiHintInternal(target, property) {
uiHintInternalSetup(target, property.toString());
}
// return the decorator
return uiHintInternal;
}
UiHint.HintRule = function (target, key, def) { return target["__" + uiHint + "__" + key] || def; };
/**
* The DisplayGroup decorator. Groups fields in auto forms; see {@link AutoFormComponent}.
* Just define a name (that appears as the group's name) and
* put the very same name on all members of the group.
*
* @param name The Name or Label that appears in forms as the groups legend.
* @param order If one uses {@link AutoFormComponent} to create a whole form from a model, this controls the groups order.
* @param description A tooltip, which can be used optionally.
*/
function DisplayGroup(name, order, description) {
if (order === void 0) { order = 0; }
function displayGroupInternalSetup(target, key) {
order = parseInt(order.toString(), 10);
// create a helper property to transport a meta data value
Object.defineProperty(target, "__isGrouped__" + key, {
value: true,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__groupName__" + key, {
value: name,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__groupOrder__" + key, {
value: order,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__groupDesc__" + key, {
value: description,
enumerable: false,
configurable: false
});
}
// the original decorator
function displayGroupInternal(target, property) {
displayGroupInternalSetup(target, property.toString());
}
// return the decorator
return displayGroupInternal;
}
/**
* The Placeholder decorator.
*
* The placeholder adds the given text as a watermark to any input fields.
* There is no function in the {@link DataGridComponent}.
*
* @param name The Name that appears in form fields as a watermark.
*/
function Placeholder(name) {
function placeholderInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__watermark__" + key, {
value: name,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__hasWatermark__" + key, {
value: true,
enumerable: false,
configurable: false
});
}
// the original decorator
function placeholderInternal(target, property) {
placeholderInternalSetup(target, property.toString());
}
// return the decorator
return placeholderInternal;
}
/**
* The TemplateHint decorator.
*
* One can define the way a property gets rendered.
* Currently supported:
* - TextArea
* - Calendar
* - Range
* - Number
* - Text
*
* The Calendar creates Date-field. However, in casde of a datatype Date the date field will be created anyway.
*
* @param template The Name that appears in form fields as a watermark.
* @param params Depending of template some additional values as a dictionary.
*/
function TemplateHint(template, params) {
function templateHintInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__templatehint__" + key, {
value: template,
enumerable: false,
configurable: false
});
if (params) {
Object.defineProperty(target, "__templatehintParams__" + key, {
value: params,
enumerable: false,
configurable: false
});
}
Object.defineProperty(target, "__hasTemplateHint__" + key, {
value: true,
enumerable: false,
configurable: false
});
}
// the original decorator
function templateHintInternal(target, name) {
templateHintInternalSetup(target, name);
}
// return the decorator
return templateHintInternal;
}
var isHidden = 'isHidden';
/**
* The Hidden decorator.
*
* The {@link DataGrid} does not show columns for properties tagged with {@link `Hidden`} decorator.
* Fields in forms that render automatically
* using the {@link `EditorComponent`} will render as `<input type="hidden">`.
*
* @param hide Optional, default is `true`.
*/
function Hidden(hide) {
if (hide === void 0) { hide = true; }
function hiddenInternalSetup(target, key, hide) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__" + isHidden + "__" + key, {
value: hide,
enumerable: false,
configurable: false
});
}
// the original decorator
function hiddenInternal(target, property) {
hiddenInternalSetup(target, property.toString(), hide);
}
// return the decorator
return hiddenInternal;
}
Hidden.IsHidden = function (target, key, def) {
if (def === void 0) { def = false; }
return target["__" + isHidden + "__" + key] || def;
};
var isSortable = 'isSortable';
var hasSortCallback = 'sortCallback';
/**
* The Sortable decorator.
*
* The {@link `DataGrid` does not sort columns for properties tagged with}`@Sortable(false)`.
* The default is that all columsn are sortable. Either avoid this decorator or use `@Sortable(true)`.
* Additionally, if the decorator is provided, you can add a sort function callback as second parameter.
*
* @param canSort Suppress or allow sorting.
* @param sortCallback An optional callback that provides a sort instruction. If omitted, `Array.prototype.sort` is being used.
*/
function Sortable(canSort, sortCallback) {
function sortableInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__" + isSortable + "__" + key, {
value: canSort,
enumerable: false,
configurable: false
});
Object.defineProperty(target, "__" + hasSortCallback + "__" + key, {
value: sortCallback,
enumerable: false,
configurable: false
});
}
// the original decorator
function sortableInternal(target, property) {
sortableInternalSetup(target, property.toString());
}
// return the decorator
return sortableInternal;
}
Sortable.IsSortable = function (target, key, def) { return target["__" + isSortable + "__" + key] || def; };
Sortable.SortCallback = function (target, key, def) { return target["__" + hasSortCallback + "__" + key] || def; };
/**
* The Readonly decorator. The field is readonly in the form. It just renders grayed out
* and handles the internals using default HTML5 techniques.
*
*
* @param readonly Optional, default is true.
* @param description A tooltip that can be used optionally.
*/
function Readonly(readonly) {
if (readonly === void 0) { readonly = true; }
function readonlyInternalSetup(target, key) {
// create a helper property to transport a meta data value
Object.defineProperty(target, "__isReadonly__" + key, {
value: readonly,
enumerable: false,
configurable: false
});
}
// the original decorator
function readonlyInternal(target, property) {
readonlyInternalSetup(target, property.toString());
}
// return the decorator
return readonlyInternal;
}
/**
* The FormatPipe decorator. Provide the name of a Pipe that's being used by the
* dynamic pipe formatter. Hence, the form does not need to apply forms manually.
* The reason is that you may create forms automatically and hence can't write
* actual Pipes somewhere. This applies especially if you create a table and loop
* through properties.
*
* @param pipe The name of the pipe's type.
* @param pipeParams The custom pipe's parameters. This is optional and can be omitted.
*
* @example
* @FormatPipe(SomePipe)
* public string formattedProperty = '';
*/
function FormatPipe(pipe, pipeParams) {
if (pipeParams === void 0) { pipeParams = null; }
function formatInternalSetup(target, key, innerPipe, innerPipeParams) {
if (innerPipeParams === void 0) { innerPipeParams = null; }
// create a helper property to transport a meta data value
Object.defineProperty(target, "__uipipe__" + key, {
value: innerPipe,
enumerable: false,
configurable: false
});
if (innerPipeParams && innerPipeParams.length) {
Object.defineProperty(target, "__pipeparams__" + key, {
value: innerPipeParams,
enumerable: false,
configurable: false
});
}
}
// the original decorator
function formatInternal(target, property) {
formatInternalSetup(target, property.toString(), pipe, pipeParams);
}
// return the decorator
return formatInternal;
}
/**
* A custom validator to valdiate a range of numbers or dates.
* This is internally to support the infarstructure and not intended to being used by custom code.
*
* @param p The field's name
*
*/
function validateRange(f, t) {
return function (c) {
if ((Number(f) || Number(t)) && Number(c.value)) {
var fr = Number(f);
var to = Number(t);
var v = Number(c.value);
return (!fr || v >= fr) && (!to || v <= to)
? null
: {
range: {
valid: false
}
};
}
if ((Date.parse(f.toString()) || Date.parse(t.toString())) && Date.parse(c.value)) {
var fr = Date.parse(f.toString());
var to = Date.parse(t.toString());
var v = Date.parse(c.value);
return (!fr || v >= fr) && (!to || v <= to)
? null
: {
range: {
valid: false
}
};
}
};
}
/**
* A custom validator to compare two fields. This is internally to support the infrastructure
* and not intended to being used by custom code.
*
* @param p The field's name
*
*/
function validateCompare(p) {
var changeEventWasAdded = false;
return function (c) {
var form = c.root;
if (form && form.controls && !changeEventWasAdded) {
form.controls[p].valueChanges.subscribe(function () {
// trigger validation for particular element
c.updateValueAndValidity();
});
changeEventWasAdded = true;
}
if (c.value) {
// compare the current value with the referenced control's value
return !c.value || c.value === c.root['controls'][p].value
? null
: {
compare: {
valid: false
}
};
}
};
}
/**
* The form validator service creates a {@link FormGroup} object from a viewmodel. If the viewmodel
* has been decorated with validation decorators the validators are created accordingly.
*
* The simplest way is creating a class with properties and add decorators, such as
* {@link StringLength}. The service will than create a {@link FormGroup} that contains a}validator
* of type {@link StringLength} for the property the decorator is written}on.
*
* The decorators provide properties for additional information, such as a custom error message.
*
*/
var FormValidatorService = /** @class */ (function () {
function FormValidatorService(fb) {
this.fb = fb;
}
/**
* Call this method to actually create the FormGroup object. Provide a valid model type.
*
* @param target A valid model type.
* @returns A FormGroup with validators
*/
FormValidatorService.prototype.build = function (target) {
var valGroup = {};
var errGroup = {};
var form;
var targetInstance;
if (target) {
// the cast is just to suppress TS errors and shows it's intentionally
try {
targetInstance = new target();
}
catch (ex) {
console.error('Invalid viewmodel for FormValidatorService');
}
}
if (targetInstance) {
// tslint:disable-next-line:forin
for (var propName in targetInstance) {
var validators = new Array();
var errmsgs = new Object();
var isRequired = "__isRequired__" + propName in target.prototype;
if (isRequired) {
(errmsgs)['required'] = target.prototype["__errRequired__" + propName];
validators.push(forms.Validators.required);
}
var hasMaxLength = "__hasMaxLength__" + propName in target.prototype;
if (hasMaxLength) {
(errmsgs)['maxlength'] = target.prototype["__errMaxLength__" + propName];
var maxLength = parseInt(target.prototype["__hasMaxLength__" + propName], 10);
validators.push(forms.Validators.maxLength(maxLength));
}
var hasMinLength = "__hasMinLength__" + propName in target.prototype;
if (hasMinLength) {
(errmsgs)['minlength'] = target.prototype["__errMinLength__" + propName];
var minLength = parseInt(target.prototype["__hasMinLength__" + propName], 10);
validators.push(forms.Validators.minLength(minLength));
}
var hasPattern = "__hasPattern__" + propName in target.prototype;
if (hasPattern) {
(errmsgs)['pattern'] = target.prototype["__errPattern__" + propName];
var pattern = new RegExp(target.prototype["__hasPattern__" + propName]);
validators.push(forms.Validators.pattern(pattern));
}
var hasRangeFrom = "__hasRangeFrom__" + propName in target.prototype;
var hasRangeTo = "__hasRangeTo__" + propName in target.prototype;
if (hasRangeFrom || hasRangeTo) {
(errmsgs)['range'] = target.prototype["__errRange__" + propName];
var f = Number(target.prototype["__hasRangeFrom__" + propName]);
var t = Number(target.prototype["__hasRangeTo__" + propName]);
if (!f && !t) {
// If NaN assume Date
f = Date.parse(f.toString());
t = Date.parse(t.toString());
}
validators.push(validateRange(f, t));
}
var hasCompare = "__hasCompareProperty__" + propName in target.prototype;
if (hasCompare) {
(errmsgs)['compare'] = target.prototype["__errCompareProperty__" + propName];
var compare = target.prototype["__withCompare__" + propName];
validators.push(validateCompare(compare));
}
if (validators.length === 0) {
// even if there is no validator we need to add the property to the group
(valGroup)[propName] = [target[propName]];
}
if (validators.length === 1) {
(valGroup)[propName] = [target[propName] || '', validators[0]];
}
if (validators.length >= 1) {
(valGroup)[propName] = [target[propName] || '', forms.Validators.compose(validators)];
}
(errGroup)[propName] = errmsgs;
}
// create form group
form = this.fb.group(valGroup);
// forward the model to the editors for easy access to other decorators
// the cast is just to suppress TS errors and shows it's intentionally
(form)['__editorModel__'] = targetInstance;
// register controls and add messages
// tslint:disable-next-line:forin
for (var propName in errGroup) {
var ctrl = form.controls[propName];
if (!ctrl) {
continue; // control might not be in the form
}
(form.controls[propName])['messages'] = (errGroup)[propName];
}
}
// return FormGroup for immediate usage
return form;
};
return FormValidatorService;
}());
FormValidatorService.decorators = [
{ type: core.Injectable }
];
FormValidatorService.ctorParameters = function () { return [
{ type: forms.FormBuilder, decorators: [{ type: core.Inject, args: [forms.FormBuilder,] }] }
]; };
/**
* The pagination component creates a few buttons to navigate a grid. The underlaying model
* is going to handle the date on the client. The pagination does not support a server backend,
* all relevant data must be loaded first.
*
* Example of usage:
* @example
* ```html
* <ac-pagination></ac-pagination>
* ```
*
* <example-url>/#/widget/grid</example-url>
*/
var DataGridPaginationComponent = /** @class */ (function () {
function DataGridPaginationComponent() {
/**
* An event fired once the user has changed the page by clicking a button.
*/
this.pageNumberChanged = new core.EventEmitter();
this.currentPageNumber = 1;
}
DataGridPaginationComponent.prototype.ngOnInit = function () {
this.setCurrentPage(1);
};
DataGridPaginationComponent.prototype.ngOnChanges = function (changes) {
var _this = this;
if (changes['maxPageIndex']) {
var change = changes['maxPageIndex'];
if (this.currentPageNumber > change.currentValue) {
// throws ExpressionChangedAfterItHasBeenCheckedException
// if there's no setTimeout.
// no need to add setTimeout if ngOnChanges
// is fired after changes made on root component.
setTimeout(function () { return _this.setCurrentPage(1); }, 1);
}
}
};
DataGridPaginationComponent.prototype.setCurrentPage = function (pageNumber, event) {
if (event) {
event.preventDefault();
}
if (pageNumber === 0 || pageNumber > this.maxPageIndex
|| pageNumber === this.currentPageNumber) {
return;
}
this.pageNumberChanged.emit(pageNumber);
this.currentPageNumber = pageNumber;
};
DataGridPaginationComponent.prototype.range = function (min, max) {
var result = new Array();
for (var i = min; i <= max; i++) {
result.push(i);
}
return result;
};
Object.defineProperty(DataGridPaginationComponent.prototype, "pageStartNumber", {
get: function () {
var startNumber = this.currentPageNumber <= 4
? 1
: this.currentPageNumber >= this.maxPageIndex - 3
? this.maxPageIndex - 6
: this.currentPageNumber - 3;
return startNumber < 1 ? 1 : startNumber;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridPaginationComponent.prototype, "pageEndNumber", {
get: function () {
var pageEnd = this.pageStartNumber + 6;
return pageEnd > this.maxPageIndex ? this.maxPageIndex : pageEnd;
},
enumerable: false,
configurable: true
});
return DataGridPaginationComponent;
}());
DataGridPaginationComponent.decorators = [
{ type: core.Component, args: [{
selector: 'ac-datagrid-pagination',
template: "<div>\n <ul class=\"pagination float-right\" [ngClass]=\"{ 'pagination-sm': size == 'sm', 'pagination-lg': size == 'lg' }\">\n <li [class.disabled]=\"currentPageNumber === 1 || !maxPageIndex\" class=\"page-item\">\n <a href (click)=\"setCurrentPage(1, $event)\" aria-label=\"Previous\" class=\"page-link\">\n <span aria-hidden=\"true\">\u00AB</span>\n </a>\n </li>\n <li [class.disabled]=\"currentPageNumber === 1 || !maxPageIndex\" class=\"page-item\">\n <a\n href\n aria-label=\"Previous\"\n (click)=\"setCurrentPage(currentPageNumber - 1, $event)\"\n class=\"page-link\">\n <span aria-hidden=\"true\">\u2039</span>\n </a>\n </li>\n <li\n *ngFor=\"let index of range(pageStartNumber, pageEndNumber)\"\n [class.active]=\"currentPageNumber === index\"\n class=\"page-item\">\n <a href (click)=\"setCurrentPage(index, $event)\" class=\"page-link\">\n <span aria-hidden=\"true\">{{ index }}</span>\n </a>\n </li>\n <li [class.disabled]=\"currentPageNumber === maxPageIndex || !maxPageIndex\">\n <a class=\"page-link\"\n href\n (click)=\"setCurrentPage(currentPageNumber + 1, $event)\"\n aria-label=\"Last\"\n >\n <span aria-hidden=\"true\">\u203A</span>\n </a>\n </li>\n <li [class.disabled]=\"currentPageNumber === maxPageIndex || !maxPageIndex\" class=\"page-item\">\n <a href (click)=\"setCurrentPage(maxPageIndex, $event)\" aria-label=\"Last\" class=\"page-link\">\n <span aria-hidden=\"true\">\u00BB</span>\n </a>\n </li>\n </ul>\n</div>\n",
styles: [""]
},] }
];
DataGridPaginationComponent.propDecorators = {
maxPageIndex: [{ type: core.Input }],
pageNumberChanged: [{ type: core.Output }],
size: [{ type: core.Input }]
};
/**
* @ignore
*/
Object.same = function (source, target) {
if (source === target) {
return true;
}
if (!(source instanceof Object) || !(target instanceof Object)) {
return false;
}
// if they are not strictly equal, they both need to be Objects
for (var prop in source) {
if (!source.hasOwnProperty(prop)) {
continue;
}
if (source[prop] === undefined || source[prop] === null || source[prop] === '') {
continue;
}
if (typeof source[prop] === 'object' && Object.same(source[prop], target[prop])) {
continue;
}
if (typeof source[prop] === 'string' && target[prop].startsWith(source[prop])) {
continue;
}
if (source[prop] === target[prop]) {
continue;
}
return false;
}
return true;
};
/**
* @ignore
*/
Object.equals = function (x, y) {
if (x === y) {
return true;
}
// if both x and y are null or undefined and exactly the same
if (!(x instanceof Object) || !(y instanceof Object)) {
return false;
}
// if they are not strictly equal, they both need to be Objects
if (x.constructor !== y.constructor) {
return false;
}
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
for (var p in x) {
if (!x.hasOwnProperty(p)) {
continue;
}
// other properties were tested using x.constructor === y.constructor
if (!y.hasOwnProperty(p)) {
return false;
}
// allows to compare x[ p ] and y[ p ] when set to undefined
if (x[p] === y[p]) {
continue;
}
// if they have the same strict value or identity then they are equal
if (typeof (x[p]) !== 'object') {
return false;
}
// Numbers, Strings, Functions, Booleans must be strictly equal
if (!Object.equals(x[p], y[p])) {
return false;
}
// Objects and Arrays must be tested recursively
}
for (var p in y) {
if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
return false;
}
// allows x[ p ] to be set to undefined
}
return true;
};
/**
* Describe a header field with name, tooltip and other properties.
*/
var DataGridHeaderModel = /** @class */ (function () {
/**
* The ctor
* @param text The text to display.
* @param desc A tooltip that is shown on mouseover (using the `title` attribute).
* @param prop The propertie's internal name.
* @param hidden optionally set a field as hidden and hence do not show in the grid.
*/
function DataGridHeaderModel(text, desc, prop, hidden) {
if (hidden === void 0) { hidden = false; }
this.text = text;
this.desc = desc;
this.prop = prop;
this.hidden = hidden;
this.isSortable = true;
this.templateHint = 'text';
}
return DataGridHeaderModel;
}());
(function (Direction) {
Direction[Direction["Ascending"] = 0] = "Ascending";
Direction[Direction["Descending"] = 1] = "Descending";
})(exports.Direction || (exports.Direction = {}));
/**
* The controlling class for Grid applications.
*
* This class takes an array of elements and handles:
* - visible headers, managed by @Hidden() decorator
* - create header titles, managed by @Display() decorator
* - sorting
* - filtering
* - count total rows
* - paging
*/
var DataGridModel = /** @class */ (function () {
function DataGridModel(items, type, pageSize) {
if (pageSize === void 0) { pageSize = 10; }
/**
* The search value filters the rows. Provide the property name and the filter instruction. Search is pure client.
*/
this.searchValue = {};
this.currentPageIndex = 1;
/**
* Event fired if user clicks Edit button.
*/
this.onEdit = new core.EventEmitter();
/**
* Event fired if user clicks Delete button.
*/
this.onDelete = new core.EventEmitter();
/**
* Current sort direction per column.
*/
this.sortDirection = {};
this._items = items;
this.pageSize = pageSize;
var typeInstance = new type();
if (typeInstance) {
// make header from decorators, omit if null
this.createHeadersForType(typeInstance);
}
}
Object.defineProperty(DataGridModel.prototype, "totalRows", {
/**
* Returns the number of rows regardless the actual filter (the total).
*/
get: function () {
return this._items.length;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "totalFilteredRows", {
get: function () {
return this.itemsFiltered ? this.itemsFiltered.length : 0;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "currentRowStart", {
get: function () {
return this.totalRows > this.pageSize ? this.startRow + 1 : this.totalRows === 0 ? 0 : 1;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "currentRowEnd", {
get: function () {
return this.startRow + this.pageSize < this.totalRows ? this.startRow + this.pageSize : this.totalRows;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "startRow", {
get: function () {
if (this.currentPageIndex === 0) {
return 0;
}
return (this.currentPageIndex - 1) * this.pageSize;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "maxPageIndex", {
get: function () {
var index = Math.ceil(this.totalFilteredRows / this.pageSize);
return index;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "items", {
get: function () {
return this._items;
},
set: function (value) {
this._items = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "itemsFiltered", {
get: function () {
var _this = this;
// not actually a filter present
if (!this.searchValue || (Object.keys(this.searchValue).length === 0 && this.searchValue.constructor === Object)) {
return this.items;
}
return this.items.filter(function (item) {
// tslint:disable-next-line:forin
for (var s in _this.searchValue) {
var pattern = new RegExp(_this.searchValue[s]);
if (pattern.test(item[s])) {
return true;
}
}
return false;
});
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "itemsOnCurrentPage", {
get: function () {
return this.itemsFiltered.slice(this.startRow, this.startRow + this.pageSize);
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "headers", {
/**
* Get all headers (column names) and their properties.
*/
get: function () {
return this._headers.filter(function (h) { return !h.hidden; });
},
enumerable: false,
configurable: true
});
Object.defineProperty(DataGridModel.prototype, "headersNotVisible", {
/**
* Returns the columns currently not shown. {@link addColumn and @see removeColumn for more}information.
*/
get: function () {
return this._headers.filter(function (h) { return h.hidden; });
},
enumerable: false,
configurable: true
});
/**
* Simple sort fucntion that makes a array sort call for the specified column.
* @param colName The column which has to be sorted after.
* // tslint:disable-next-line:max-line-length
* @param dir The order, descended is *desc*, any other string is ascending.
* If nothing is provided, the direction toggles. Initital value is *ascending*.
*/
DataGridModel.prototype.sortColumn = function (colName, dir, sortCallback) {
if (!dir) {
// if nothing is provided, toggle current
dir = this.sortDirection[colName] === exports.Direction.Ascending ? exports.Direction.Descending : exports.Direction.Ascending;
}
// remember last and update UI
this.sortDirection[colName] = dir;
if (sortCallback) {
this.items.sort(sortCallback);
}
else {
this.items.sort(function (a, b) {
if (dir === exports.Direction.Descending) {
return a[colName] > b[colName] ? 1 : -1;
}
else {
return a[colName] > b[colName] ? -1 : 1;
}
});