UNPKG

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
(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 &lt;th&gt; 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; } });