Splitted form component
diff --git a/views/ngXosLib/gulp/ngXosHelpers.js b/views/ngXosLib/gulp/ngXosHelpers.js
index f29a646..638e665 100644
--- a/views/ngXosLib/gulp/ngXosHelpers.js
+++ b/views/ngXosLib/gulp/ngXosHelpers.js
@@ -85,6 +85,7 @@
       module: {
         glob: [
           options.xosHelperSource + '*.js',
+          options.xosHelperSource + 'services/helpers/**/*.js',
           options.xosHelperSource + 'services/*.js',
           options.xosHelperSource + 'ui_components/**/*.js'
         ],
@@ -115,9 +116,7 @@
   gulp.task('docs', ['makeDocs', 'serveDocs'], function(){
     
     var files = [
-      options.xosHelperSource + '*.js',
-      options.xosHelperSource + 'services/*.js',
-      options.xosHelperSource + 'ui_components/**/*.js'
+      options.xosHelperSource + '**/*.js'
     ];
 
     gulp.watch(files, ['makeDocs']);
diff --git a/views/ngXosLib/karma.conf.js b/views/ngXosLib/karma.conf.js
index 9d880a0..18c6f6f 100644
--- a/views/ngXosLib/karma.conf.js
+++ b/views/ngXosLib/karma.conf.js
@@ -94,7 +94,7 @@
     // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
     browsers: [
       'PhantomJS',
-      // 'Chrome'
+      'Chrome'
     ],
 
 
diff --git a/views/ngXosLib/xosHelpers/spec/ui/field.test.js b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
new file mode 100644
index 0000000..9e7a7f2
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
@@ -0,0 +1,80 @@
+/**
+ * © OpenCORD
+ *
+ * Created by teone on 5/25/16.
+ */
+
+(function () {
+  'use strict';
+
+  let element, scope, isolatedScope, rootScope, compile;
+  const compileElement = () => {
+
+    if(!scope){
+      scope = rootScope.$new();
+    }
+
+    element = angular.element('<xos-field name="name" field="field" ng-model="ngModel"></xos-field>');
+    compile(element)(scope);
+    scope.$digest();
+    isolatedScope = element.isolateScope().vm;
+  }
+
+  describe('The xos.helper module', function(){
+
+    describe('The xosField component', () => {
+
+      beforeEach(module('xos.helpers'));
+
+      beforeEach(inject(function ($compile, $rootScope) {
+        compile = $compile;
+        rootScope = $rootScope;
+      }));
+
+      it('should throw an error if no name is passed', inject(($compile, $rootScope) => {
+        function errorFunctionWrapper(){
+          // setup the parent scope
+          scope = $rootScope.$new();
+          scope.field = {
+            label: 'Label',
+            type: 'number',
+            validators: {}
+          };
+          scope.ngModel = {
+            label: 1
+          };
+          compileElement();
+        }
+        expect(errorFunctionWrapper).toThrow(new Error('[xosField] Please provide a field name'));
+      }));
+
+      it('should throw an error if no field definition is passed', inject(($compile, $rootScope) => {
+        function errorFunctionWrapper(){
+          // setup the parent scope
+          scope = $rootScope.$new();
+          scope.name = 'label';
+          scope.ngModel = {
+            label: 1
+          };
+          compileElement();
+        }
+        expect(errorFunctionWrapper).toThrow(new Error('[xosField] Please provide a field definition'));
+      }));
+
+      it('should throw an error if no field model is passed', inject(($compile, $rootScope) => {
+        function errorFunctionWrapper(){
+          // setup the parent scope
+          scope = $rootScope.$new();
+          scope.name = 'label';
+          scope.field = {
+            label: 'Label',
+            type: 'number',
+            validators: {}
+          };
+          compileElement();
+        }
+        expect(errorFunctionWrapper).toThrow(new Error('[xosField] Please provide an ng-model'));
+      }));
+    });
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/spec/ui/form.test.js b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
index 226a62a..7673fc8 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/form.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
@@ -9,6 +9,7 @@
 
   describe('The xos.helper module', function(){
 
+    // TODO move in separate file
     describe('The XosFormHelper service', () => {
       let service;
 
@@ -122,6 +123,16 @@
           expect(service._getFieldFormat(new Date())).toEqual('date');
           expect(service._getFieldFormat('2016-04-19T23:09:10.208092Z')).toEqual('date');
         });
+
+        it('should return array', () => {
+          expect(service._getFieldFormat([])).toEqual('array');
+          expect(service._getFieldFormat(['a', 'b'])).toEqual('array');
+        });
+
+        it('should return object', () => {
+          expect(service._getFieldFormat({})).toEqual('object');
+          expect(service._getFieldFormat({foo: 'bar'})).toEqual('object');
+        });
       });
 
       it('should convert the fields array in an empty form object', () => {
@@ -274,10 +285,11 @@
             enabled: true,
             role: 'user', //select
             a_permissions: [
-
             ],
-            o_permissions: {
-
+            object_field: {
+              string: 'bar',
+              number: 1,
+              email: 'teo@onlab.us'
             }
           };
 
@@ -292,14 +304,15 @@
           expect(isolatedScope.excludedField).toEqual(expected);
         });
 
-        it('should render 8 input field', () => {
+        xit('should render 8 input field', () => {
           // boolean are in the form model, but are not input
           expect(Object.keys(isolatedScope.formField).length).toEqual(9);
           var field = element[0].getElementsByTagName('input');
-          expect(field.length).toEqual(8);
+          expect(field.length).toEqual(10);
         });
 
         it('should render 1 boolean field', () => {
+          // console.log($(element).find('.boolean-field'));
           expect($(element).find('.boolean-field > button').length).toEqual(2)
         });
 
@@ -336,7 +349,7 @@
             expect(isolatedScope.ngModel.enabled).toEqual(false);
           });
 
-          it('should change value to false', () => {
+          it('should change value to true', () => {
             isolatedScope.ngModel.enabled = false;
             scope.$apply();
             expect(isolatedScope.ngModel.enabled).toEqual(false);
@@ -397,6 +410,10 @@
             expect(isolatedScope.testForm.age.$error.min).toBeTruthy();
           });
         });
+
+        describe('the object field', () => {
+          
+        });
       });
     });
   });
diff --git a/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
new file mode 100644
index 0000000..ce52272
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
@@ -0,0 +1,149 @@
+(function () {
+  
+  angular.module('xos.uiComponents')
+
+  /**
+  * @ngdoc service
+  * @name xos.uiComponents.XosFormHelpers
+  * @requires xos.uiComponents.LabelFormatter
+  * @requires xos.helpers._
+  **/
+
+  .service('XosFormHelpers', function(_, LabelFormatter){
+
+    /**
+    * @ngdoc method
+    * @name xos.uiComponents.XosFormHelpers#_isEmail
+    * @methodOf xos.uiComponents.XosFormHelpers
+    * @description
+    * Return true if the string is an email address
+    * @param {string} text The string to be evaluated
+    * @returns {boolean} If the string match an email format
+    **/
+
+    this._isEmail = (text) => {
+      var re = /(([^<>()[\]\\.,;:\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,}))/;
+      return re.test(text);
+    };
+
+    /**
+    * @ngdoc method
+    * @name xos.uiComponents.XosFormHelpers#_getFieldFormat
+    * @methodOf xos.uiComponents.XosFormHelpers
+    * @description
+    * Return the type of the input
+    * @param {mixed} value The data to be evaluated
+    * @returns {string} The type of the input
+    **/
+
+    this._getFieldFormat = (value) => {
+
+      if(angular.isArray(value)){
+        return 'array';
+      }
+
+      // check if is date
+      if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && new Date(value).getTime() > 631180800000)){
+        return 'date';
+      }
+
+      // check if is boolean
+      // isNaN(false) = false, false is a number (0), true is a number (1)
+      if(typeof value  === 'boolean'){
+        return 'boolean';
+      }
+
+      // check if a string is a number
+      if(!isNaN(value) && value !== null){
+        return 'number';
+      }
+
+      // check if a string is an email
+      if(this._isEmail(value)){
+        return 'email';
+      }
+
+      // if null return string
+      if(value === null){
+        return 'string';
+      }
+
+      return typeof value;
+    };
+
+    /**
+    * @ngdoc method
+    * @name xos.uiComponents.XosFormHelpers#buildFormStructure
+    * @methodOf xos.uiComponents.XosFormHelpers
+    * @description
+    * Return the type of the input
+    * @param {object} modelField An object containing one property for each field of the model
+    * @param {object} customField An object containing one property for each field custom field
+    * @param {object} model The actual model on wich build the form structure (it is used to determine the type of the input)
+    * @returns {object} An object describing the form structure in the form of:
+    * ```
+    * {
+    *   'field-name': {
+    *     label: 'Label',
+    *     type: 'number', //typeof field
+    *     validators: {} // see xosForm for more details
+    *   }
+    * }
+    * ```
+    **/
+
+    this.buildFormStructure = (modelField, customField, model) => {
+
+      modelField = Object.keys(modelField).length > 0 ? modelField : customField; //if no model field are provided, check custom
+      customField = customField || {};
+
+      return _.reduce(Object.keys(modelField), (form, f) => {
+
+        form[f] = {
+          label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
+          type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
+          validators: (customField[f] && customField[f].validators) ? customField[f].validators : {}
+        };
+
+        if(form[f].type === 'date'){
+          model[f] = new Date(model[f]);
+        }
+
+        if(form[f].type === 'number'){
+          model[f] = parseInt(model[f], 10);
+        }
+
+        return form;
+      }, {});
+    };
+
+    /**
+    * @ngdoc method
+    * @name xos.uiComponents.XosFormHelpers#parseModelField
+    * @methodOf xos.uiComponents.XosFormHelpers
+    * @description
+    * Helpers for buildFormStructure, convert a list of model properties in an object used to build the form structure, eg:
+    * ```
+    * // input:
+    * ['id', 'name'm 'mail']
+    *
+    * // output
+    * {
+    *   id: {},
+    *   name: {},
+    *   mail: {}
+    * }
+    * ```
+    * @param {array} fields An array of fields representing the model properties
+    * @returns {object} An object containing one property for each field of the model
+    **/
+
+    this.parseModelField = (fields) => {
+      return _.reduce(fields, (form, f) => {
+        form[f] = {};
+        return form;
+      }, {});
+    }
+
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/label_formatter.service.js b/views/ngXosLib/xosHelpers/src/services/helpers/ui/label_formatter.service.js
similarity index 95%
rename from views/ngXosLib/xosHelpers/src/services/label_formatter.service.js
rename to views/ngXosLib/xosHelpers/src/services/helpers/ui/label_formatter.service.js
index 3b63ecd..bb1d279 100644
--- a/views/ngXosLib/xosHelpers/src/services/label_formatter.service.js
+++ b/views/ngXosLib/xosHelpers/src/services/helpers/ui/label_formatter.service.js
@@ -3,7 +3,7 @@
 
   /**
   * @ngdoc service
-  * @name xos.helpers.LabelFormatter
+  * @name xos.uiComponents.LabelFormatter
   * @description This factory define a set of helper function to format label started from an object property
   **/
 
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
new file mode 100644
index 0000000..923def6
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
@@ -0,0 +1,89 @@
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 5/25/16.
+ */
+
+(function () {
+  'use strict';
+
+  angular.module('xos.uiComponents')
+  /**
+    * @ngdoc directive
+    * @name xos.uiComponents.directive:xosField
+    * @restrict E
+    * @description The xos-field directive.
+    * This component decide, give a field wich kind of input it need to print.
+    * @param {string} name The field name
+    * @param {object} field The field configuration:
+    * ```
+    * {
+    *   label: 'Label',
+    *   type: 'number', //typeof field
+    *   validators: {} // see xosForm for more details
+    * }
+    * ```
+    * @param {mixed} ngModel The field value
+    */
+  .directive('xosField', function(){
+    return {
+      restrict: 'E',
+      scope: {
+        name: '=',
+        field: '=',
+        ngModel: '='
+      },
+      template: `
+        <label>{{vm.field.label}}</label>
+            <input
+              ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object'"
+              type="{{vm.field.type}}"
+              name="{{vm.name}}"
+              class="form-control"
+              ng-model="vm.ngModel"
+              ng-minlength="vm.field.validators.minlength || 0"
+              ng-maxlength="vm.field.validators.maxlength || 2000"
+              ng-required="vm.field.validators.required || false" />
+            <span class="boolean-field" ng-if="vm.field.type === 'boolean'">
+              <button
+                class="btn btn-success"
+                ng-show="vm.ngModel"
+                ng-click="vm.ngModel = false">
+                <i class="glyphicon glyphicon-ok"></i>
+              </button>
+              <button
+                class="btn btn-danger"
+                ng-show="!vm.ngModel"
+                ng-click="vm.ngModel = true">
+                <i class="glyphicon glyphicon-remove"></i>
+              </button>
+            </span>
+            <div
+              class="panel panel-default object-field"
+              ng-if="vm.field.type == 'object' "
+              >
+              <div class="panel-heading">Panel heading without title</div>
+              <div class="panel-body">
+                Panel content
+              </div>
+            </div>
+      `,
+      bindToController: true,
+      controllerAs: 'vm',
+      controller: function(){
+        // console.log('Field: ', this.name, this.field.type, this.ngModel);
+        if(!this.name){
+          throw new Error('[xosField] Please provide a field name');
+        }
+        if(!this.field){
+          throw new Error('[xosField] Please provide a field definition');
+        }
+        if(!this.ngModel){
+          throw new Error('[xosField] Please provide an ng-model');
+        }
+      }
+    }
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
index 5294229..db4a2fd 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
@@ -48,6 +48,9 @@
     * ```
     * @element ANY
     * @scope
+    * @requires xos.uiComponents.directive:xosField
+    * @requires xos.uiComponents.XosFormHelpers
+    * @requires xos.helpers._
     * @example
     
     Autogenerated form
@@ -159,31 +162,7 @@
       template: `
         <ng-form name="vm.{{vm.config.formName || 'form'}}">
           <div class="form-group" ng-repeat="(name, field) in vm.formField">
-            <label>{{field.label}}</label>
-            <input
-              ng-if="field.type !== 'boolean'"
-              type="{{field.type}}"
-              name="{{name}}"
-              class="form-control"
-              ng-model="vm.ngModel[name]"
-              ng-minlength="field.validators.minlength || 0"
-              ng-maxlength="field.validators.maxlength || 2000"
-              ng-required="field.validators.required || false" />
-            <span class="boolean-field" ng-if="field.type === 'boolean'">
-              <button
-                class="btn btn-success"
-                ng-show="vm.ngModel[name]"
-                ng-click="vm.ngModel[name] = false">
-                <i class="glyphicon glyphicon-ok"></i>
-              </button>
-              <button
-                class="btn btn-danger"
-                ng-show="!vm.ngModel[name]"
-                ng-click="vm.ngModel[name] = true">
-                <i class="glyphicon glyphicon-remove"></i>
-              </button>
-            </span>
-            <!-- <pre>{{vm[vm.config.formName][name].$error | json}}</pre> -->
+            <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>
             <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
           </div>
           <div class="form-group" ng-if="vm.config.actions">
@@ -233,76 +212,5 @@
 
       }
     }
-  })
-  .service('XosFormHelpers', function(_, LabelFormatter){
-
-    this._isEmail = (text) => {
-      var re = /(([^<>()[\]\\.,;:\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,}))/;
-      return re.test(text);
-    };
-
-    this._getFieldFormat = (value) => {
-
-      // check if is date
-      if (_.isDate(value) || (!Number.isNaN(Date.parse(value)) && new Date(value).getTime() > 631180800000)){
-        return 'date';
-      }
-
-      // check if is boolean
-      // isNaN(false) = false, false is a number (0), true is a number (1)
-      if(typeof value  === 'boolean'){
-        return 'boolean';
-      }
-
-      // check if a string is a number
-      if(!isNaN(value) && value !== null){
-        return 'number';
-      }
-
-      // check if a string is an email
-      if(this._isEmail(value)){
-        return 'email';
-      }
-
-      // if null return string
-      if(value === null){
-        return 'string';
-      }
-
-      return typeof value;
-    };
-
-    this.buildFormStructure = (modelField, customField, model) => {
-
-      modelField = Object.keys(modelField).length > 0 ? modelField : customField; //if no model field are provided, check custom
-      customField = customField || {};
-
-      return _.reduce(Object.keys(modelField), (form, f) => {
-
-        form[f] = {
-          label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
-          type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
-          validators: (customField[f] && customField[f].validators) ? customField[f].validators : {}
-        };
-
-        if(form[f].type === 'date'){
-          model[f] = new Date(model[f]);
-        }
-
-        if(form[f].type === 'number'){
-          model[f] = parseInt(model[f], 10);
-        }
-
-        return form;
-      }, {});
-    };
-
-    this.parseModelField = (fields) => {
-      return _.reduce(fields, (form, f) => {
-        form[f] = {};
-        return form;
-      }, {});
-    }
-
-  })
+  });
 })();
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
index 059f461..b3a2eec 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
@@ -249,6 +249,7 @@
             });
 
             // build form structure
+            // TODO move in a pure function for testing purposes
             props.forEach((p, i) => {
               this.formConfig.fields[p] = {
                 label: LabelFormatter.format(labels[i]).replace(':', ''),
diff --git a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
index b74ddf9..4af3680 100644
--- a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
+++ b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
@@ -27,6 +27,13 @@
         'xos.uiComponents',
       ])
       .config(config)
+
+      /**
+      * @ngdoc service
+      * @name xos.helpers._
+      * @description Wrap [lodash](https://lodash.com/docs) in an Angular Service
+      **/
+
       .factory('_', $window => $window._ );
 
   function config($httpProvider, $interpolateProvider, $resourceProvider) {