Browse Source

data manipulation events dispatch to singleton event manager

robert2206 8 years ago
parent
commit
4836b0fc41

+ 20 - 4
src/app/app.component.ts

@@ -1,5 +1,5 @@
 import { Component, ViewChild } from '@angular/core';
-import { Nav, Platform } from 'ionic-angular';
+import { Nav, Platform, Loading, LoadingController } from 'ionic-angular';
 import { StatusBar, Splashscreen } from 'ionic-native';
 
 import { LoginPage } from "../pages/login/login";
@@ -13,6 +13,8 @@ import { PhonecallsPage } from "../pages/phonecalls/phonecalls";
 import { ToolsPage } from "../pages/tools/tools";
 import { AboutPage } from "../pages/about/about";
 
+import { EventsManager } from "../base/events/event-manager";
+
 @Component({
   templateUrl: 'app.html'
 })
@@ -22,10 +24,12 @@ export class MyApp {
     rootPage: any = LoginPage;
     homePage: any = HomePage;
     entries: Array<{ visible: boolean, title: string, pages: Array<{ visible: boolean, title: string, icon: string, component: any, params?: any }> }>;
+    loader: Loading;
 
-    pages: Array<{title: string, component: any}>;
-
-    constructor(public platform: Platform) {
+    constructor(
+        public platform: Platform,
+        public loadingCtrl: LoadingController
+    ) {
         this.initializeApp();
 
         this.entries = [
@@ -129,6 +133,18 @@ export class MyApp {
         this.platform.ready().then(() => {
             StatusBar.styleDefault();
             Splashscreen.hide();
+
+            EventsManager.subscribe("app:loading", data => { 
+                if (data) {
+                    this.loader = this.loadingCtrl.create({
+                        content: "Cargando, espere..."
+                    });
+                    
+                    this.loader.present();
+                } else {
+                    this.loader.dismiss();
+                }
+            });
         });
     }
 

+ 3 - 0
src/app/app.module.ts

@@ -22,6 +22,7 @@ import { AboutPage } from "../pages/about/about";
 
 // Detail Pages
 import { CustomerPage } from "../pages/customer/customer";
+import { ProductPage } from "../pages/product/product";
 
 // Services
 import { OdooRPCService } from "angular2-odoo-jsonrpc";
@@ -53,6 +54,7 @@ import { DoubleTap } from "../directives/double-tap";
         AboutPage,
         // Detail Pages
         CustomerPage,
+        ProductPage,
         // Components
         OMasterHeader,
         OMasterFooter,
@@ -81,6 +83,7 @@ import { DoubleTap } from "../directives/double-tap";
         PhonecallsPage,
         // Detail Pages
         CustomerPage,
+        ProductPage,
         // Other Pages
         ToolsPage,
         AboutPage,

+ 12 - 3
src/base/base-details-view.ts

@@ -2,6 +2,8 @@ import { BaseView } from "./base-view";
 import { PouchService } from "../services/pouch-service";
 import { Observable } from "rxjs/Observable";
 
+import { EventsManager } from "../base/events/event-manager";
+
 export abstract class BaseDetailsView<T> extends BaseView<T> {
 
     item: T;
@@ -11,7 +13,7 @@ export abstract class BaseDetailsView<T> extends BaseView<T> {
         super(c, PouchService);
         
         this.item = item;
-        this.action = item ? "to_update" : "to_create";
+        this.action = this.itemExists() ? "to_update" : "to_create";
     }
 
     /**
@@ -62,8 +64,15 @@ export abstract class BaseDetailsView<T> extends BaseView<T> {
      * 
      * @param data 
      */
-    performSave(): Observable<any> {
+    performSave(): void {
         this.setItem(Object.assign(this.getItem(), { odoo_model: super.getModelName(), odoo_status: this.getAction() }));
-        return super.getInjectable(PouchService).save(this.getItem());
+        super.getInjectable(PouchService).save(this.getItem()).subscribe(result => {
+            EventsManager.publish("app:changed", {
+                action: this.getAction() === "to_create" ? "add" : "update",
+                item: Object.assign(this.getItem(), { _id: result.id, _rev: result.rev })
+            });
+        }, error => {
+            EventsManager.publish("app:error", error);
+        });
     }
 }

+ 41 - 10
src/base/base-list-view.ts

@@ -1,6 +1,9 @@
 import { BaseView } from "./base-view";
 import { PouchService } from "../services/pouch-service";
 import { Observable } from "rxjs/Observable";
+import { Observer } from "rxjs/Observer";
+
+import { EventsManager } from "../base/events/event-manager";
 
 export abstract class BaseListView<T> extends BaseView<T>{
 
@@ -22,18 +25,40 @@ export abstract class BaseListView<T> extends BaseView<T>{
      * 
      */
     initialize() {
-        let pouch = super.getInjectable(PouchService);
+        EventsManager.publish("app:loading", true);
 
-        pouch.getAll(this.getModelName()).subscribe(result => {
+        super.getInjectable(PouchService).getAll(this.getModelName()).subscribe(result => {
             this.applyFilter(result.docs);            
+            this.subcribeToEvents();
+
+            EventsManager.publish("app:loading", false);
         }, error => { 
             console.log(error);
+
+            EventsManager.publish("app:loading", false);
+            EventsManager.publish("app:error", error);
+        });
+    }
+
+    /**
+     * 
+     */
+    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);
         });
-        
-        pouch.changes().subscribe(result => {
-            console.log("esto es un cambio");
-            console.log(result);
-        }, error => console.log(error));
     }
 
     /**
@@ -86,9 +111,15 @@ export abstract class BaseListView<T> extends BaseView<T>{
     /**
      * 
      */
-    performDelete(): Observable<any> {
-        // return Observable.empty();
-        return super.getInjectable(PouchService).remove(this.getSelectedItem());
+    performDelete(): void {
+        super.getInjectable(PouchService).remove(this.getSelectedItem()).subscribe(result => { 
+            EventsManager.publish("app:changed", {
+                action: "delete",
+                details: result
+            });
+        }, error => { 
+            EventsManager.publish("app:error", error);
+        });
     }
 
     /**

+ 6 - 18
src/base/base-view.ts

@@ -1,6 +1,5 @@
 import { ReflectiveInjector } from "@angular/core";
 import { getMetadataStorage } from "../odoo/utils/metadata-storage";
-import { Events } from "ionic-angular";
 
 export class BaseView<T> {
 
@@ -12,31 +11,20 @@ export class BaseView<T> {
     constructor(c: { new (): T; }, ...injectables: any[]) {
         this.type = new c();
         this.model = getMetadataStorage().models.filterByTarget(this.type.constructor).items.shift();
-        this.injector = ReflectiveInjector.resolveAndCreate(injectables.concat(Events));
+        this.injector = ReflectiveInjector.resolveAndCreate(injectables);
         this.title = "Sin título";
     }
 
-    /**
-     * 
-     * @param data 
-     */
-    publishEvent(data: any): void {
-
-    }
-
-    /**
-     * 
-     */
-    subscribeToEvent(): void {
-
-    }
-
     /**
      * 
      * @param injectable 
      */
     getInjectable(injectable: any): any {
-        return this.injector.get(injectable);
+        try {
+            return this.injector.get(injectable);
+        } catch (error) {
+            return undefined;
+        }
     }
 
     /**

+ 44 - 0
src/base/events/event-manager.ts

@@ -0,0 +1,44 @@
+import { ReflectiveInjector } from "@angular/core";
+import { Events } from "ionic-angular";
+import { EventSlots } from "./event-slot";
+
+export abstract class EventsManager {
+
+    private static EVENTS_INSTANCE;
+
+    /**
+     * 
+     */
+    public static getManager(): Events {
+        if (!EventsManager.EVENTS_INSTANCE) {
+            EventsManager.EVENTS_INSTANCE = ReflectiveInjector.resolveAndCreate([Events]).get(Events);
+        }
+
+        return EventsManager.EVENTS_INSTANCE;
+    }
+
+    /**
+     * 
+     * @param name 
+     * @param data 
+     */
+    public static publish(name: string , data?: any): void {
+        EventsManager.getManager().publish(name, data);
+    }
+
+    /**
+     * 
+     * @param name 
+     */
+    public static subscribe(name: string, handler: Function): any {
+        EventsManager.getManager().subscribe(name, handler);
+    }
+
+    /**
+     * 
+     * @param name 
+     */
+    public static unsubscribe(name: string): void {
+        EventsManager.getManager().unsubscribe(name);
+    }
+}

+ 7 - 0
src/base/events/event-slot.ts

@@ -0,0 +1,7 @@
+export type EventSlot = "app:loading"|"app:changed"|"app:error";
+
+export abstract class EventSlots {
+    static LOADING: EventSlot = "app:loading";
+    static CHANGED: EventSlot = "app:changed";
+    static ERROR: EventSlot = "app:error";
+}

+ 2 - 8
src/pages/customer/customer.ts

@@ -21,8 +21,6 @@ export class CustomerPage extends BaseDetailsView<Partner>{
         super(Partner, navParams.data); 
 
         this.customerForm = this.formBuilder.group({
-            _id: null,
-            _rev: null,
             name: ["", Validators.required],
             city: ["", Validators.maxLength(35)],
             street: ["", Validators.maxLength(35)],
@@ -55,11 +53,7 @@ export class CustomerPage extends BaseDetailsView<Partner>{
         }
 
         super.setItem(Object.assign(this.getItem(), data, { customer: true }));
-
-        super.performSave().subscribe(result => {
-            this.navCtrl.pop();
-        }, error => {
-            console.log(error);
-        });
+        super.performSave();
+        this.navCtrl.pop();
     }
 }

+ 14 - 5
src/pages/customers/customers.ts

@@ -18,18 +18,28 @@ export class CustomersPage extends BaseListView<Partner> {
         public alertController: AlertController
     ) {
         super(Partner, ["customer", "=", true]);
-     }
+    }
 
+    /**
+     * 
+     */
     ionViewDidLoad() {
         console.log('ionViewDidLoad CustomersPage');
     }
 
+    /**
+     * 
+     */
+    ionViewDidEnter() {
+        super.setSelectedIndex(-1);
+    }
+
     /**
      * 
      * @param item
      */
     openOptions(item: Partner): void {
-        this.setSelectedItem(item);
+        super.setSelectedItem(item);
         
         this.actionSheetCtrl.create({
             title: "Opciones",
@@ -67,8 +77,7 @@ export class CustomersPage extends BaseListView<Partner> {
                     text: "Cancel",
                     role: "cancel",
                     handler: () => {
-                        console.log("Canceled");
-                        
+                        super.setSelectedIndex(-1);
                     }
                 },
             ]
@@ -96,7 +105,7 @@ export class CustomersPage extends BaseListView<Partner> {
                 {
                     text: "Aceptar",
                     handler: () => {
-                        super.performDelete().subscribe(result => console.log(result), error => console.log(error));
+                        super.performDelete();
                     }
                 }
             ]

+ 26 - 0
src/pages/product/product.html

@@ -0,0 +1,26 @@
+<ion-header>
+    <ion-navbar>
+        <ion-title>Producto</ion-title>
+        <ion-buttons end>
+            <button ion-button type="submit" form="details-form" [disabled]="productForm.invalid">
+                GUARDAR
+            </button>
+        </ion-buttons>
+    </ion-navbar>
+</ion-header>
+<ion-content>
+    <form id="details-form" [formGroup]="productForm" (ngSubmit)="submit(productForm.value)">
+        <ion-item>
+            <ion-label floating>Nombre</ion-label>
+            <ion-input type="text" formControlName="name"></ion-input>
+        </ion-item>
+        <ion-item>
+            <ion-label floating>EAN13</ion-label>
+            <ion-input type="text" formControlName="ean13"></ion-input>
+        </ion-item>
+        <ion-item>
+            <ion-label floating>Referencia interna</ion-label>
+            <ion-input type="text" formControlName="defaultCode"></ion-input>
+        </ion-item>
+    </form>
+</ion-content>

+ 3 - 0
src/pages/product/product.scss

@@ -0,0 +1,3 @@
+page-product {
+
+}

+ 51 - 0
src/pages/product/product.ts

@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { Validators, FormGroup, FormBuilder } from "@angular/forms";
+import { NavController, NavParams } from 'ionic-angular';
+
+import { ProductTemplate } from "../../odoo/models/product.template";
+import { BaseDetailsView } from "../../base/base-details-view";
+
+@Component({
+    selector: 'page-product',
+    templateUrl: 'product.html'
+})
+export class ProductPage extends BaseDetailsView<ProductTemplate>{
+
+    productForm: FormGroup;
+
+    constructor(
+        public navCtrl: NavController,
+        public navParams: NavParams,
+        public formBuilder: FormBuilder
+    ) { 
+        super(ProductTemplate, navParams.data);
+
+        this.productForm = this.formBuilder.group({
+            name: ["", Validators.required],
+            ean13: ["", Validators.maxLength(35)],
+            defaultCode: ["", Validators.maxLength(35)],
+        });
+
+        if (super.itemExists()) {
+            this.productForm.patchValue(super.getItem());
+        }
+    }
+
+    ionViewDidLoad() {
+        console.log('ionViewDidLoad ProductPage');
+    }
+
+     /**
+     * 
+     * @param data 
+     */
+    submit(data: any) {
+        if (this.productForm.invalid) {
+            return;
+        }
+
+        super.setItem(Object.assign(this.getItem(), data));
+        super.performSave();
+        this.navCtrl.pop();
+    }
+}

+ 2 - 3
src/pages/products/products.html

@@ -6,10 +6,9 @@
         <ion-title>Productos</ion-title>
     </ion-navbar>
 </ion-header>
-
 <ion-content>
     <ion-list [virtualScroll]="getItems()" approxItemHeight="97px">
-		<ion-item *virtualItem="let item">
+		<ion-item *virtualItem="let item" (doubleTap)="openOptions(item)">
 			<ion-thumbnail item-left>
                 <img src="./assets/images/product.png" *ngIf="!item.image_medium"/>
 				<img [src]="item.image_medium | sanitizeUrl" *ngIf="item.image_medium"/>
@@ -26,7 +25,7 @@
 		</ion-item>
 	</ion-list>
 	<ion-fab right bottom>
-        <button ion-fab>
+        <button ion-fab (click)="goToDetails()">
             <ion-icon name="add"></ion-icon>
         </button>
     </ion-fab>

+ 78 - 2
src/pages/products/products.ts

@@ -1,8 +1,9 @@
 import { Component } from '@angular/core';
-import { NavController, NavParams } from 'ionic-angular';
+import { NavController, NavParams, ActionSheetController, AlertController } from 'ionic-angular';
 
 import { BaseListView } from "../../base/base-list-view";
 import { ProductTemplate } from "../../odoo/models/product.template";
+import { ProductPage } from "../product/product";
 
 @Component({
     selector: 'page-products',
@@ -12,7 +13,9 @@ export class ProductsPage extends BaseListView<ProductTemplate> {
 
     constructor(
         public navCtrl: NavController,
-        public navParams: NavParams
+        public navParams: NavParams,
+        public actionSheetCtrl: ActionSheetController,
+        public alertController: AlertController
     ) { 
         super(ProductTemplate);
     }
@@ -24,4 +27,77 @@ export class ProductsPage extends BaseListView<ProductTemplate> {
         console.log('ionViewDidLoad ProductsPage');
     }
 
+    /**
+     * 
+     */
+    ionViewDidEnter() {
+        super.setSelectedIndex(-1);
+    }
+
+    /**
+     * 
+     * @param item
+     */
+    openOptions(item: ProductTemplate): void {
+        super.setSelectedItem(item);
+        
+        this.actionSheetCtrl.create({
+            title: "Opciones",
+            buttons: [
+                {
+                    text: "Abrir",
+                    icon: "open",
+                    handler: () => {
+                        this.goToDetails();
+                    }
+                },
+                {
+                    text: "Eliminar",
+                    icon: "close",
+                    role: "destructive",
+                    handler: () => {
+                        this.askIfDelete();
+                    }
+                },
+                {
+                    text: "Cancel",
+                    role: "cancel",
+                    handler: () => {
+                        super.setSelectedIndex(-1);
+                    }
+                },
+            ]
+        }).present();
+    }
+
+    /**
+     * 
+     */
+    goToDetails(): void {
+        console.log("CLICK");
+        
+        this.navCtrl.push(ProductPage, super.getSelectedItem());
+    }
+
+    /**
+     * 
+     */
+    askIfDelete(): void {
+        this.alertController.create({
+            title: "Confirmar",
+            message: "Quieres eliminar este producto?",
+            buttons: [
+                {
+                    text: "Cancelar"
+                },
+                {
+                    text: "Aceptar",
+                    handler: () => {
+                        super.performDelete();
+                    }
+                }
+            ]
+        }).present();
+    }
+
 }