Merge "[CORD-1896] GUI become irresponsive"
diff --git a/src/app/core/table/table.html b/src/app/core/table/table.html
index b7aaa96..6a128c7 100644
--- a/src/app/core/table/table.html
+++ b/src/app/core/table/table.html
@@ -28,7 +28,7 @@
         <table ng-class="vm.classes">
         <thead>
         <tr>
-            <th ng-repeat="col in vm.config.columns">
+            <th ng-repeat="col in ::vm.config.columns">
                 {{col.label}}
                 <div ng-if="vm.config.order">
                     <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">
diff --git a/src/app/core/table/table.ts b/src/app/core/table/table.ts
index 6c8d95c..32043a7 100644
--- a/src/app/core/table/table.ts
+++ b/src/app/core/table/table.ts
@@ -159,7 +159,7 @@
   controllerAs: 'vm',
   controller: TableCtrl,
   bindings: {
-    data: '=',
+    data: '<',
     config: '='
   }
 };
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index 7ca0ae1..7735ff0 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -46,6 +46,7 @@
   }
 
   public query(modelName: string, apiUrl?: string): Observable<any> {
+    this.$log.debug(`[XosModelStore] QUERY: ${modelName}`);
     // if there isn't already an observable for that item
     // create a new one and .next() is called by this.loadInitialData once data are received
     if (!this._collections[modelName]) {
@@ -54,6 +55,7 @@
     }
     // else manually trigger the next with the last know value to trigger the subscribe method of who's requesting this data
     else {
+      this.$log.debug(`[XosModelStore] QUERY: Calling "next" on: ${modelName}`);
       this.efficientNext(this._collections[modelName]);
     }
 
@@ -100,28 +102,21 @@
   }
 
   public get(modelName: string, modelId: string | number): Observable<any> {
-    const subject = new BehaviorSubject([]);
+    this.$log.debug(`[XosModelStore] GET: ${modelName} [${modelId}]`);
+    const subject = new BehaviorSubject({});
 
-    const _findModel = (subject) => {
-      this._collections[modelName]
-        .subscribe((res) => {
-          const model = _.find(res, {id: modelId});
-          if (model) {
-            subject.next(model);
-          }
-        });
-    };
+    if (angular.isString(modelId)) {
+      modelId = parseInt(modelId, 10);
+    }
 
-    if (!this._collections[modelName]) {
-      // cache the models in that collection
-      this.query(modelName)
-        .subscribe((res) => {
-          _findModel(subject);
-        });
-    }
-    else {
-      _findModel(subject);
-    }
+    this.query(modelName)
+      .subscribe((res) => {
+        const model = _.find(res, {id: modelId});
+        if (model) {
+          this.$log.debug(`[XosModelStore] GET: Calling "next" on: ${modelName} [${modelId}]`);
+          subject.next(model);
+        }
+      });
 
     return subject.asObservable();
   }
diff --git a/src/app/views/crud/crud.relations.service.ts b/src/app/views/crud/crud.relations.service.ts
index f6a7015..092dda8 100644
--- a/src/app/views/crud/crud.relations.service.ts
+++ b/src/app/views/crud/crud.relations.service.ts
@@ -23,6 +23,7 @@
 import {IXosFormCfg} from '../../core/form/form';
 import {IXosTableCfg} from '../../core/table/table';
 import {IXosConfigHelpersService} from '../../core/services/helpers/config.helpers';
+import {Subscription} from 'rxjs';
 
 interface IXosCrudRelationBaseTabData {
   model: any;
@@ -64,7 +65,7 @@
 
   public getModel (r: IXosModelRelation, id: string | number): Promise<IXosCrudRelationFormTabData> {
     const d = this.$q.defer();
-    this.XosModelStore.get(r.model, id)
+    const subscription: Subscription = this.XosModelStore.get(r.model, id)
       .subscribe(
         item => {
           this.$log.debug(`[XosCrud] Loaded manytoone relation with ${r.model} on ${r.on_field}`, item);
@@ -76,6 +77,8 @@
           };
 
           d.resolve(data);
+
+          subscription.unsubscribe();
         },
         err => d.reject
       );
@@ -84,7 +87,6 @@
 
   public getModels(r: IXosModelRelation, source_id: string | number): Promise<IXosCrudRelationTableTabData> {
     const d = this.$q.defer();
-
     this.XosModelStore.query(r.model)
       .subscribe(
         items => {
diff --git a/src/app/views/crud/crud.ts b/src/app/views/crud/crud.ts
index bcac6a9..b4434c8 100644
--- a/src/app/views/crud/crud.ts
+++ b/src/app/views/crud/crud.ts
@@ -27,6 +27,7 @@
 import {IXosCrudRelationService} from './crud.relations.service';
 import {IXosDebugService, IXosDebugStatus} from '../../core/debug/debug.service';
 import {IXosKeyboardShortcutService} from '../../core/services/keyboard-shortcut';
+import {Subscription} from 'rxjs';
 
 export interface IXosModelRelation {
   model: string;
@@ -73,6 +74,8 @@
   };
   public debugTab: boolean;
 
+  private subscription: Subscription;
+
   constructor(
     private $scope: angular.IScope,
     private $log: angular.ILogService,
@@ -109,24 +112,6 @@
       this.$scope.$apply();
     });
 
-    this.store.query(this.data.model)
-      .subscribe(
-        (event) => {
-          // NOTE Observable mess with $digest cycles, we need to schedule the expression later
-          $scope.$evalAsync(() => {
-            this.tableData = event;
-
-            // if it is a detail page for an existing model
-            if ($stateParams['id'] && $stateParams['id'] !== 'add') {
-              this.related.onetomany = _.filter($state.current.data.relations, {type: 'onetomany'});
-              this.related.manytoone = _.filter($state.current.data.relations, {type: 'manytoone'});
-              this.model = _.find(this.tableData, {id: parseInt($stateParams['id'], 10)});
-              this.getRelatedModels(this.related, this.model);
-            }
-          });
-        }
-      );
-
     // if it is a detail page
     if ($stateParams['id']) {
       this.list = false;
@@ -138,6 +123,21 @@
         const resource = this.ModelRest.getResource(endpoint);
         this.model = new resource({});
       }
+      else {
+        this.subscription = this.store.get(this.data.model, $stateParams['id'])
+          .first(val => {
+            // NOTE emit an event only if we have an object, and only the first time we have it
+            return Object.keys(val).length > 0;
+          })
+          .subscribe(res => {
+            $scope.$evalAsync(() => {
+              this.related.onetomany = _.filter($state.current.data.relations, {type: 'onetomany'});
+              this.related.manytoone = _.filter($state.current.data.relations, {type: 'manytoone'});
+              this.model = res;
+              this.getRelatedModels(this.related, this.model);
+            });
+          });
+      }
 
       this.XosKeyboardShortcut.registerKeyBinding({
         key: 'A',
@@ -195,9 +195,24 @@
       //   },
       //   description: 'Clear selected item'
       // }, 'view');
+
+      this.subscription = this.store.query(this.data.model)
+        .subscribe(
+          (event) => {
+            // NOTE Observable mess with $digest cycles, we need to schedule the expression later
+            $scope.$evalAsync(() => {
+              this.tableData = event;
+            });
+          }
+        );
     }
   }
 
+  $onDestroy() {
+    this.subscription.unsubscribe();
+    this.$log.info(`[XosCrud] Destroying component`);
+  }
+
   public iterateItems() {
     const rowCount = this.tableCfg.filteredData.length > this.tableCfg.pagination.pageSize ? this.tableCfg.pagination.pageSize : this.tableCfg.filteredData.length;
     if ((this.tableCfg.selectedRow + 1) < rowCount) {