|
@@ -8,22 +8,235 @@ import { EventsManager } from "../base/events/event-manager";
|
|
|
export abstract class BaseListView<T> extends BaseView<T>{
|
|
|
|
|
|
items: T[];
|
|
|
- selectedIndex: number;
|
|
|
+ itemsShown: T[];
|
|
|
+ itemsMatched: T[];
|
|
|
+ selectedItem: T;
|
|
|
filters: Array<[string, string, any]>;
|
|
|
+ search: boolean;
|
|
|
+ range: { start: number, end: number };
|
|
|
|
|
|
constructor(c: { new (): T; }, ...filters: Array<[string, string, any]>) {
|
|
|
super(c, PouchService);
|
|
|
|
|
|
this.items = [];
|
|
|
- this.selectedIndex = -1;
|
|
|
+ this.itemsShown = [];
|
|
|
+ this.itemsMatched = [];
|
|
|
+ this.selectedItem = null;
|
|
|
this.filters = filters;
|
|
|
-
|
|
|
+ this.search = false;
|
|
|
+ this.range = { start: 0, end: 10 }
|
|
|
+
|
|
|
this.initialize();
|
|
|
}
|
|
|
|
|
|
+ //-------------------------------------------------------------------------------
|
|
|
+ // BEGIN GETTERS AND SETTERS |
|
|
|
+ //-------------------------------------------------------------------------------
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getItems(): T[] {
|
|
|
+ return this.items;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param items
|
|
|
+ */
|
|
|
+ setItems(items: T[]): void {
|
|
|
+ this.items = items;
|
|
|
+ this.setItemsShown(this.getItems().slice(this.getRange().start, this.getRange().end));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getItemsShown(): T[] {
|
|
|
+ return this.itemsShown;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param itemsShown
|
|
|
+ */
|
|
|
+ setItemsShown(itemsShown: T[]): void {
|
|
|
+ this.itemsShown = itemsShown;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getItemsMatched(): T[] {
|
|
|
+ return this.itemsMatched;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param itemsMatched
|
|
|
+ */
|
|
|
+ setItemsMatched(itemsMatched: T[]): void {
|
|
|
+ this.itemsMatched = itemsMatched;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param item
|
|
|
+ */
|
|
|
+ setSelectedItem(item: T): void {
|
|
|
+ this.selectedItem = item;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getSelectedItem(): T {
|
|
|
+ return this.selectedItem;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
*
|
|
|
*/
|
|
|
+ getFilters(): Array<[string, string, any]> {
|
|
|
+ return this.filters;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param filters
|
|
|
+ */
|
|
|
+ setFilters(filters: Array<[string, string, any]>): void {
|
|
|
+ this.filters = filters;
|
|
|
+
|
|
|
+ this.applyFilter(this.getItems());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ isSearch(): boolean {
|
|
|
+ return this.search;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param search
|
|
|
+ */
|
|
|
+ setSearch(search: boolean): void {
|
|
|
+ this.search = search;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getRange(): { start: number, end: number } {
|
|
|
+ return this.range;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param range
|
|
|
+ */
|
|
|
+ setRange(range: { start: number, end: number }): void {
|
|
|
+ this.range = range;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ------------------------------- SPECIAL GETTERS -------------------------------
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getStartRange(): number {
|
|
|
+ return this.getRange().start;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getEndRange(): number {
|
|
|
+ return this.getRange().end;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ hasItems(): boolean {
|
|
|
+ return this.getItems().length > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ hasItemsShown(): boolean {
|
|
|
+ return this.getItemsShown().length > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ isExpandableRange(): boolean {
|
|
|
+ return this.getRange().end >= this.getItemsShown().length;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getSelectedIndexInAll(): number {
|
|
|
+ if (!this.getSelectedItem()) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.getItems().indexOf(this.getSelectedItem());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getSelectedIndexInShown(): number {
|
|
|
+ if (!this.getSelectedItem()) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.getItemsShown().indexOf(this.getSelectedItem());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ getSelectedIndexInMatched(): number {
|
|
|
+ if (!this.getSelectedItem()) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.getItemsMatched().indexOf(this.getSelectedItem());
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---------------------------- SPECIAL SETTERS -----------------------------
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param start
|
|
|
+ */
|
|
|
+ setStartRange(start: number): void {
|
|
|
+ this.setRange({ start: start, end: this.getRange().end });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param end
|
|
|
+ */
|
|
|
+ setEndRange(end: number): void {
|
|
|
+ this.setRange({ start: this.getRange().start, end: end });
|
|
|
+ }
|
|
|
+
|
|
|
+ //---------------------------------------------------------------------------
|
|
|
+ // BEGIN SERVICES |
|
|
|
+ //---------------------------------------------------------------------------
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Inicializa la lista.
|
|
|
+ * Emite una evento al empezar y terminar el proceso.
|
|
|
+ */
|
|
|
initialize() {
|
|
|
EventsManager.publish("app:loading", true);
|
|
|
|
|
@@ -41,28 +254,7 @@ export abstract class BaseListView<T> extends BaseView<T>{
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
- */
|
|
|
- private subcribeToEvents(): void {
|
|
|
- EventsManager.subscribe("app:changed", data => {
|
|
|
- if (data.action === "add") {
|
|
|
- this.items.unshift(data.item);
|
|
|
- }
|
|
|
-
|
|
|
- if (data.action === "update") {
|
|
|
- this.items[this.getSelectedIndex()] = data.item;
|
|
|
- }
|
|
|
-
|
|
|
- if (data.action === "delete") {
|
|
|
- this.items.splice(this.getSelectedIndex(), 1);
|
|
|
- }
|
|
|
-
|
|
|
- this.setSelectedIndex(-1);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- *
|
|
|
+ * Aplica los filtros correspondientes a la lista
|
|
|
*/
|
|
|
applyFilter(records: T[]): void {
|
|
|
if (records.length == 0) {
|
|
@@ -70,12 +262,12 @@ export abstract class BaseListView<T> extends BaseView<T>{
|
|
|
}
|
|
|
|
|
|
if (this.getFilters().length == 0) {
|
|
|
- this.items = records;
|
|
|
+ this.setItems(records);
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- this.items = records.filter(record => {
|
|
|
+ this.setItems(records.filter(record => {
|
|
|
let expressions = this.getFilters().map(filter => {
|
|
|
let operator = "===";
|
|
|
let leftOperand = "record." + filter[0];
|
|
@@ -105,111 +297,178 @@ export abstract class BaseListView<T> extends BaseView<T>{
|
|
|
});
|
|
|
|
|
|
return eval(expressions.join("&&"));
|
|
|
- });
|
|
|
+ }));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- *
|
|
|
+ * Suscribe a los eventos de la lista
|
|
|
*/
|
|
|
- performDelete(): void {
|
|
|
- super.getInjectable(PouchService).remove(this.getSelectedItem()).subscribe(result => {
|
|
|
- EventsManager.publish("app:changed", {
|
|
|
- action: "delete",
|
|
|
- details: result
|
|
|
- });
|
|
|
- }, error => {
|
|
|
- EventsManager.publish("app:error", error);
|
|
|
+ private subcribeToEvents(): void {
|
|
|
+ EventsManager.subscribe("app:changed", data => {
|
|
|
+ if (data.action === "add") {
|
|
|
+ this.add(data.item);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.action === "update") {
|
|
|
+ this.update(data.item);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data.action === "delete") {
|
|
|
+ this.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.unselectItem();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Adicioana a la lista un nuevo objeto guardado
|
|
|
+ * @param obj
|
|
|
*/
|
|
|
- getItems(): T[] {
|
|
|
- return this.items;
|
|
|
+ add(item: T): void {
|
|
|
+ this.items.unshift(item);
|
|
|
+
|
|
|
+ if (this.getItemsShown().length < this.getEndRange()) {
|
|
|
+ this.itemsShown.unshift(item);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- *
|
|
|
- * @param items
|
|
|
+ * Actualiza el objeto modificado
|
|
|
+ * @param item
|
|
|
*/
|
|
|
- setItems(items: T[]): void {
|
|
|
- this.items = items;
|
|
|
+ update(item: T): void {
|
|
|
+ this.itemsShown[this.getSelectedIndexInShown()] = item;
|
|
|
|
|
|
- this.applyFilter(this.getItems());
|
|
|
+ if (this.isSearch()) {
|
|
|
+ this.itemsMatched[this.getSelectedIndexInShown()] = item;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.items[this.getSelectedIndexInAll()] = item;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Quita el objeto borrado de la lista
|
|
|
*/
|
|
|
- getSelectedIndex(): number {
|
|
|
- return this.selectedIndex;
|
|
|
+ remove(): void {
|
|
|
+ this.itemsShown.splice(this.getSelectedIndexInShown(), 1);
|
|
|
+
|
|
|
+ if (this.isSearch()) {
|
|
|
+ this.itemsMatched.splice(this.getSelectedIndexInShown(), 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.items.splice(this.getSelectedIndexInAll(), 1);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
- * @param index
|
|
|
+ * Deselecciona el elemento que haya sido previamente seleccionado.
|
|
|
*/
|
|
|
- setSelectedIndex(selectedIndex: number): void {
|
|
|
- this.selectedIndex = selectedIndex;
|
|
|
+ unselectItem(): void {
|
|
|
+ this.setSelectedItem(null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
- * @param item
|
|
|
+ * Limpia las coincidencias de búsqueda.
|
|
|
*/
|
|
|
- indexOf(item: T): number {
|
|
|
- return this.getItems().indexOf(item);
|
|
|
+ clearItemsMatched(): void {
|
|
|
+ this.setItemsMatched([]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Alterna entre el modo de lista y el modo de búsqueda de la lista.
|
|
|
*/
|
|
|
- getSelectedItem(): T {
|
|
|
- if (this.getSelectedIndex() === -1) {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ toggleSearch(): void {
|
|
|
+ this.setSearch(!this.isSearch());
|
|
|
+ this.setRange({ start: 0, end: 10 });
|
|
|
|
|
|
- return this.getItems()[this.getSelectedIndex()];
|
|
|
+ if (!this.isSearch()) {
|
|
|
+ this.setItemsMatched([]);
|
|
|
+ this.setItemsShown(this.getItems().slice(this.getRange().start, this.getRange().end));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Realiza la expansión del rango para la vista.
|
|
|
*/
|
|
|
- hasItems(): boolean {
|
|
|
- return this.getItems().length > 0;
|
|
|
- }
|
|
|
+ expandRange(): void {
|
|
|
+ this.setRange({ start: this.getRange().end, end: this.getRange().end + 10 });
|
|
|
|
|
|
- /**
|
|
|
- *
|
|
|
- * @param item
|
|
|
- */
|
|
|
- setSelectedItem(item: T): void {
|
|
|
- let index = this.indexOf(item);
|
|
|
- this.setSelectedIndex(index);
|
|
|
+ if (this.isSearch()) {
|
|
|
+ this.setItemsShown(this.getItemsShown().concat(this.getItemsMatched().slice(this.getRange().start, this.getRange().end)));
|
|
|
+ } else {
|
|
|
+ this.setItemsShown(this.getItemsShown().concat(this.getItems().slice(this.getRange().start, this.getRange().end)));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
+ * Realiza la expasión de la vista de la lista.
|
|
|
*
|
|
|
+ * @param event
|
|
|
*/
|
|
|
- deselectItem(): void {
|
|
|
- this.setSelectedIndex(-1);
|
|
|
+ expand(event: any): void {
|
|
|
+ this.expandRange();
|
|
|
+
|
|
|
+ if (this.isExpandableRange()) {
|
|
|
+ event.enable(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ event.complete();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ * Realiza la busqueda cada campo de los objetos en la lista de items mientras este campo sea de tipo string.
|
|
|
+ * Emite un evento cuando comienza y termina el proceso.
|
|
|
+ * @param text
|
|
|
*/
|
|
|
- getFilters(): Array<[string, string, any]> {
|
|
|
- return this.filters;
|
|
|
+ performSearch(event: any): void {
|
|
|
+ let value: string = event.target.value;
|
|
|
+
|
|
|
+ if (!value || value.length < 3) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ EventsManager.publish("app:loading", true);
|
|
|
+
|
|
|
+ this.clearItemsMatched();
|
|
|
+
|
|
|
+ let regExp = new RegExp(value, "g");
|
|
|
+ let item: T = null;
|
|
|
+ let keys: Array<any> = null;
|
|
|
+
|
|
|
+ for (let i = 0; i < this.getItems().length; i++) {
|
|
|
+ item = this.getItems()[i];
|
|
|
+ keys = Object.keys(item);
|
|
|
+
|
|
|
+ for (let j = 0; j < keys.length; j++) {
|
|
|
+ if (typeof item[keys[j]] !== "string") {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!!item[keys[j]].toLowerCase().match(regExp)) {
|
|
|
+ this.getItemsMatched().push(item);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setItemsShown(this.getItemsMatched().slice(this.getRange().start, this.getRange().end));
|
|
|
+
|
|
|
+ EventsManager.publish("app:loading", false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
- * @param filters
|
|
|
+ * Realiza el borrado del item seleccionado.
|
|
|
+ * Emite un evento al terminar el proceso.
|
|
|
*/
|
|
|
- setFilters(filters: Array<[string, string, any]>): void {
|
|
|
- this.filters = filters;
|
|
|
-
|
|
|
- this.applyFilter(this.getItems());
|
|
|
+ performDelete(): void {
|
|
|
+ super.getInjectable(PouchService).remove(this.getSelectedItem()).subscribe(result => {
|
|
|
+ EventsManager.publish("app:changed", {
|
|
|
+ action: "delete",
|
|
|
+ details: result
|
|
|
+ });
|
|
|
+ }, error => {
|
|
|
+ EventsManager.publish("app:error", error);
|
|
|
+ });
|
|
|
}
|
|
|
}
|