Validating form before sending request

Change-Id: I908ca21cbe151bd2931cbf05be8970a1294a19ee
diff --git a/src/app/core/alert/alert.html b/src/app/core/alert/alert.html
new file mode 100644
index 0000000..20f3f5c
--- /dev/null
+++ b/src/app/core/alert/alert.html
@@ -0,0 +1,6 @@
+<div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">
+    <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">
+        <span aria-hidden="true">&times;</span>
+    </button>
+    <p ng-transclude></p>
+</div>
\ No newline at end of file
diff --git a/src/app/core/alert/alert.ts b/src/app/core/alert/alert.ts
new file mode 100644
index 0000000..6c64015
--- /dev/null
+++ b/src/app/core/alert/alert.ts
@@ -0,0 +1,53 @@
+export interface IXosAlertConfig {
+  type: string;
+  closeBtn?: boolean;
+  autoHide?: number;
+}
+
+class AlertCtrl {
+
+  static $inject = ['$timeout'];
+
+  public config: any;
+  public show: boolean;
+  public dismiss: () => void;
+
+  constructor (
+    private $timeout: ng.ITimeoutService
+  ) {
+
+  }
+
+  $onInit() {
+    if (!angular.isDefined(this.config)) {
+      throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');
+    }
+
+    // default the value to true
+    this.show = this.show !== false;
+
+    this.dismiss = () => {
+      this.show = false;
+    };
+
+    console.log(this.config);
+    if (this.config.autoHide) {
+
+      let to = this.$timeout(() => {
+        this.dismiss();
+        this.$timeout.cancel(to);
+      }, this.config.autoHide);
+    }
+  }
+}
+
+export const xosAlert: angular.IComponentOptions = {
+  template: require('./alert.html'),
+  controllerAs: 'vm',
+  controller: AlertCtrl,
+  transclude: true,
+  bindings: {
+    config: '=',
+    show: '=',
+  }
+};
diff --git a/src/app/core/form/form.html b/src/app/core/form/form.html
index 2172d3a..9afd949 100644
--- a/src/app/core/form/form.html
+++ b/src/app/core/form/form.html
@@ -6,9 +6,11 @@
     <!--</div>-->
     <div class="form-group" ng-repeat="field in vm.config.inputs">
         <xos-field name="field.name" field="field" ng-model="vm.ngModel[field.name]"></xos-field>
+        <!--<xos-validation field="vm[vm.config.formName || 'form'][name]" form = "vm[vm.config.formName || 'form']"></xos-validation>-->
+        <p ng-show="(field.hint).length >0" role="alert">{{field.hint}}</p>
     </div>
     <div class="form-group" ng-if="vm.config.actions">
-        <!--<xos-alert config="vm.config.feedback" show="vm.config.feedback.show">{{vm.config.feedback.message}}</xos-alert>-->
+        <xos-alert config="vm.config.feedback" show="vm.config.feedback.show">{{vm.config.feedback.message}}</xos-alert>
         <button role="button" href=""
                 ng-repeat="action in vm.config.actions"
                 ng-click="action.cb(vm.ngModel, vm[vm.config.formName || 'form'])"
diff --git a/src/app/core/form/form.ts b/src/app/core/form/form.ts
index 34b0287..c0bd206 100644
--- a/src/app/core/form/form.ts
+++ b/src/app/core/form/form.ts
@@ -15,6 +15,8 @@
   show: boolean;
   message: string;
   type: string; // NOTE is possible to enumerate success, error, warning, info?
+  autoHide?: number;
+  closeBtn?: boolean;
 }
 
 export interface IXosFormInputValidator {
@@ -37,6 +39,7 @@
   name: string;
   label: string;
   type: string; // options are: [date, boolean, number, email, string, select],
+  hint?: string;
   validators: IXosFormInputValidator;
   options?: IXosFormInputOptions[];
 }
@@ -77,12 +80,13 @@
       throw new Error('[xosForm] Please provide a formName property in the config');
     }
 
-    // NOTE is needed ??
+    // NOTE needed to avoid xosAlert throw an error
     if (!this.config.feedback) {
       this.config.feedback =  {
         show: false,
         message: 'Form submitted successfully !!!',
-        type: 'success'
+        type: 'success',
+        closeBtn: true
       };
     }
 
diff --git a/src/app/core/index.ts b/src/app/core/index.ts
index e1a830e..17536f6 100644
--- a/src/app/core/index.ts
+++ b/src/app/core/index.ts
@@ -13,6 +13,7 @@
 import {xosForm} from './form/form';
 import {xosField} from './field/field';
 import 'angular-toastr';
+import {xosAlert} from './alert/alert';
 
 export const xosCore = 'xosCore';
 
@@ -35,4 +36,5 @@
   .component('xosLogin', xosLogin)
   .component('xosTable', xosTable)
   .component('xosForm', xosForm)
-  .component('xosField', xosField);
+  .component('xosField', xosField)
+  .component('xosAlert', xosAlert);
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 93ae064..75c7de1 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -171,13 +171,14 @@
 
   public modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[] {
 
-    return _.map(fields, f => {
+    return _.map(fields, (f: IXosModelDefsField) => {
       if (f.relation) {
-        const input = {
+        const input: IXosFormInput = {
           name: f.name,
           label: this.toLabel(f.name),
           type: 'select',
-          validators: f.validators
+          validators: f.validators,
+          hint: f.hint
         };
         this.populateSelectField(f, input);
         return input;
@@ -194,38 +195,54 @@
   }
 
   public modelToFormCfg(model: IModeldef): IXosFormConfig {
-    return {
+    const formCfg: IXosFormConfig = {
       formName: `${model.name}Form`,
       exclude: ['backend_status', 'creator'],
       actions: [{
         label: 'Save',
         class: 'success',
         icon: 'ok',
-        cb: (item, form) => {
-          const model = angular.copy(item);
-
-          // TODO remove ManyToMany relations and save them separately (how??)
-          delete item.networks;
-
-          // adding userId as creator
-          item.creator = this.AuthService.getUser().id;
-
-          item.$save()
-            .then((res) => {
-              if (res.status === 403 || res.status === 405 || res.status === 500) {
-                // TODO understand why 405 does not go directly in catch (it may be realted to ng-rest-gw)
-                throw new Error();
-              }
-              this.toastr.success(`${model.name} succesfully saved`);
-            })
-            .catch(err => {
-              // TODO keep the edited model
-              this.toastr.error(`Error while saving ${model.name}`);
-            });
-        }
+        cb: null
       }],
       inputs: this.modelFieldToInputCfg(model.fields)
     };
+
+    formCfg.actions[0].cb = (item, form: angular.IFormController) => {
+
+      if (!form.$valid) {
+        formCfg.feedback = {
+          show: true,
+          message: 'Form is invalid',
+          type: 'danger',
+          closeBtn: true
+        };
+        console.log(formCfg);
+        return;
+      }
+
+      const model = angular.copy(item);
+
+      // TODO remove ManyToMany relations and save them separately (how??)
+      delete item.networks;
+
+      // adding userId as creator
+      item.creator = this.AuthService.getUser().id;
+
+      item.$save()
+        .then((res) => {
+          if (res.status === 403 || res.status === 405 || res.status === 500) {
+            // TODO understand why 405 does not go directly in catch (it may be realted to ng-rest-gw)
+            throw new Error();
+          }
+          this.toastr.success(`${model.name} succesfully saved`);
+        })
+        .catch(err => {
+          // TODO keep the edited model
+          this.toastr.error(`Error while saving ${model.name}`);
+        });
+    };
+
+    return formCfg;
   }
 
   private fromCamelCase(string: string): string {