Updated docs
with array field

Change-Id: Ie707c58a0c38afe30fed776f0d1c16fb777b7457
diff --git a/.eslintrc b/.eslintrc
index ba3aaee..d580f3a 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -37,7 +37,9 @@
         "angular/ng_di": [0, "function or array"],
         "angular/ng_angularelement": 0,
         "angular/ng_on_watch": 1,
-        "angular/ng_controller_as_vm": 0
+        "angular/ng_controller_as_vm": 0,
+        "angular/ng_controller_as": 2,
+        "angular/ng_on_watch": 0,
     },
     "globals" :{
         "angular": true
diff --git a/dist/ngXosHelpers.min.js b/dist/ngXosHelpers.min.js
index 2c8fe7a..271d3a8 100644
--- a/dist/ngXosHelpers.min.js
+++ b/dist/ngXosHelpers.min.js
@@ -1,2 +1,2 @@
-"use strict";function _toConsumableArray(e){if(Array.isArray(e)){for(var n=0,i=Array(e.length);n<e.length;n++)i[n]=e[n];return i}return Array.from(e)}!function(){angular.module("xos.uiComponents",["chart.js","RecursionHelper","dndLists"])}(),function(){angular.module("xos.uiComponents").component("xosSmartTable",{restrict:"E",bindings:{config:"="},template:'\n        <div class="row" ng-show="vm.data.length > 0">\n          <div class="col-xs-12 text-right">\n            <a href="" class="btn btn-success" ng-click="vm.createItem()">\n              Add\n            </a>\n          </div>\n        </div>\n        <div class="row">\n          <div class="col-xs-12 table-responsive">\n            <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n          </div>\n        </div>\n        <div class="panel panel-default" ng-show="vm.detailedItem">\n          <div class="panel-heading">\n            <div class="row">\n              <div class="col-xs-11">\n                <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n                <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n              </div>\n              <div class="col-xs-1">\n                <a href="" ng-click="vm.cleanForm()">\n                  <i class="glyphicon glyphicon-remove pull-right"></i>\n                </a>\n              </div>\n            </div>\n          </div>\n          <div class="panel-body">\n            <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n          </div>\n        </div>\n        <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n        <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n      ',bindToController:!0,controllerAs:"vm",controller:["$injector","LabelFormatter","_","XosFormHelpers",function(e,n,i,o){var t=this;this.responseMsg=!1,this.responseErr=!1,this.tableConfig={columns:[],actions:[{label:"delete",icon:"remove",cb:function(e){t.Resource["delete"]({id:e.id}).$promise.then(function(){i.remove(t.data,function(n){return n.id===e.id}),t.responseMsg=t.config.resource+" with id "+e.id+" successfully deleted"})["catch"](function(n){t.responseErr=n.data.detail||"Error while deleting "+t.config.resource+" with id "+e.id})},color:"red"},{label:"details",icon:"search",cb:function(e){t.detailedItem=e}}],classes:"table table-striped table-bordered table-responsive",filter:"field",order:!0,pagination:{pageSize:10}},this.formConfig={exclude:this.config.hiddenFields,fields:{},formName:this.config.resource+"Form",actions:[{label:"Save",icon:"ok",cb:function(e){var n=void 0,i=!0;e.id?(n=e.$update(),i=!1):n=e.$save(),n.then(function(n){i&&t.data.push(angular.copy(n)),delete t.detailedItem,t.responseMsg=t.config.resource+" with id "+e.id+" successfully saved"})["catch"](function(n){t.responseErr=n.data.detail||"Error while saving "+t.config.resource+" with id "+e.id})},"class":"success"}]},this.cleanForm=function(){delete t.detailedItem},this.createItem=function(){t.detailedItem=new t.Resource},this.Resource=e.get(this.config.resource);var r=function(){t.Resource.query().$promise.then(function(e){if(!e[0])return void(t.data=e);var r=e[0],a=Object.keys(r);i.remove(a,function(e){return"id"===e||"validators"===e}),angular.isArray(t.config.hiddenFields)&&(a=i.difference(a,t.config.hiddenFields)),a.forEach(function(e){var i={label:n.format(e),prop:e};i.type=o._getFieldFormat(r[e]),t.tableConfig.columns.push(i)}),a.forEach(function(e){t.formConfig.fields[e]={label:n.format(e).replace(":",""),type:o._getFieldFormat(r[e])}}),t.data=e})};r()}]})}(),function(){angular.module("xos.uiComponents").component("xosSmartPie",{restrict:"E",bindings:{config:"="},template:'\n      <canvas\n        class="chart chart-pie {{vm.config.classes}}"\n        chart-data="vm.data" chart-labels="vm.labels"\n        chart-legend="{{vm.config.legend}}">\n      </canvas>\n    ',bindToController:!0,controllerAs:"vm",controller:["$injector","$interval","$scope","$timeout","_",function(e,n,i,o,t){var r=this;if(!this.config.resource&&!this.config.data)throw new Error("[xosSmartPie] Please provide a resource or an array of data in the configuration");var a=function(e){return t.groupBy(e,r.config.groupBy)},s=function(e){return t.reduce(Object.keys(e),function(n,i){return n.concat(e[i].length)},[])},l=function(e){return angular.isFunction(r.config.labelFormatter)?r.config.labelFormatter(Object.keys(e)):Object.keys(e)},c=function(e){var n=a(e);r.data=s(n),r.labels=l(n)};this.config.resource?!function(){r.Resource=e.get(r.config.resource);var i=function(){r.Resource.query().$promise.then(function(e){e[0]&&c(e)})};i(),r.config.poll&&n(function(){i()},1e3*r.config.poll)}():i.$watch(function(){return r.config.data},function(e){e&&c(r.config.data)},!0),i.$on("create",function(e,n){console.log("create: "+n.id)}),i.$on("destroy",function(e,n){console.log("destroy: "+n.id)})}]})}(),function(){angular.module("xos.uiComponents").component("xosValidation",{restrict:"E",bindings:{field:"=",form:"="},template:'\n      <div ng-cloak>\n        <xos-alert config="vm.config" show="vm.field.$error.required !== undefined && vm.field.$error.required !== false  && (vm.field.$touched || vm.form.$submitted)">\n          Field required\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">\n          This is not a valid email\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">\n          Too short\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">\n          Too long\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">\n          Field invalid\n        </xos-alert>\n      </div>\n    ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:function(){this.config={type:"danger"}}})}(),function(){angular.module("xos.uiComponents").component("xosTable",{restrict:"E",bindings:{data:"=",config:"="},template:'\n        <div ng-show="vm.data.length > 0 && vm.loader == false">\n          <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n            <div class="col-xs-12">\n              <input\n                class="form-control"\n                placeholder="Type to search.."\n                type="text"\n                ng-model="vm.query"/>\n            </div>\n          </div>\n          <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n            <thead>\n              <tr>\n                <th ng-repeat="col in vm.columns">\n                  {{col.label}}\n                  <span ng-if="vm.config.order">\n                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n                      <i class="glyphicon glyphicon-chevron-up"></i>\n                    </a>\n                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n                      <i class="glyphicon glyphicon-chevron-down"></i>\n                    </a>\n                  </span>\n                </th>\n                <th ng-if="vm.config.actions">Actions:</th>\n              </tr>\n            </thead>\n            <tbody ng-if="vm.config.filter == \'field\'">\n              <tr>\n                <td ng-repeat="col in vm.columns">\n                  <input\n                    ng-if="col.type !== \'boolean\' && col.type !== \'array\' && col.type !== \'object\' && col.type !== \'custom\'"\n                    class="form-control"\n                    placeholder="Type to search by {{col.label}}"\n                    type="text"\n                    ng-model="vm.query[col.prop]"/>\n                  <select\n                    ng-if="col.type === \'boolean\'"\n                    class="form-control"\n                    ng-model="vm.query[col.prop]">\n                    <option value="">-</option>\n                    <option value="true">True</option>\n                    <option value="false">False</option>\n                  </select>\n                </td>\n                <td ng-if="vm.config.actions"></td>\n              </tr>\n            </tbody>\n            <tbody>\n              <tr ng-repeat="item in vm.data | filter:vm.query:vm.comparator | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n                <td ng-repeat="col in vm.columns" xos-link-wrapper>\n                  <span ng-if="!col.type || col.type === \'text\'">{{item[col.prop]}}</span>\n                  <span ng-if="col.type === \'boolean\'">\n                    <i class="glyphicon"\n                      ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n                    </i>\n                  </span>\n                  <span ng-if="col.type === \'date\'">\n                    {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n                  </span>\n                  <span ng-if="col.type === \'array\'">\n                    {{item[col.prop] | arrayToList}}\n                  </span>\n                  <span ng-if="col.type === \'object\'">\n                    <dl class="dl-horizontal">\n                      <span ng-repeat="(k,v) in item[col.prop]">\n                        <dt>{{k}}</dt>\n                        <dd>{{v}}</dd>\n                      </span>\n                    </dl>\n                  </span>\n                  <span ng-if="col.type === \'custom\'">\n                    {{col.formatter(item)}}\n                  </span>\n                  <span ng-if="col.type === \'icon\'">\n                    <i class="glyphicon glyphicon-{{col.formatter(item)}}">\n                    </i>\n                  </span>\n                </td>\n                <td ng-if="vm.config.actions">\n                  <a href=""\n                    ng-repeat="action in vm.config.actions"\n                    ng-click="action.cb(item)"\n                    title="{{action.label}}">\n                    <i\n                      class="glyphicon glyphicon-{{action.icon}}"\n                      style="color: {{action.color}};"></i>\n                  </a>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n          <xos-pagination\n            ng-if="vm.config.pagination"\n            page-size="vm.config.pagination.pageSize"\n            total-elements="vm.data.length"\n            change="vm.goToPage">\n            </xos-pagination>\n        </div>\n        <div ng-show="(vm.data.length == 0 || !vm.data) && vm.loader == false">\n           <xos-alert config="{type: \'info\'}">\n            No data to show.\n          </xos-alert>\n        </div>\n        <div ng-show="vm.loader == true">\n          <div class="loader"></div>\n        </div>\n      ',bindToController:!0,controllerAs:"vm",controller:["_","$scope","Comparator",function(e,n,i){var o=this;if(this.comparator=i,this.loader=!0,n.$watch(function(){return o.data},function(e){angular.isDefined(e)&&(o.loader=!1)}),!this.config)throw new Error('[xosTable] Please provide a configuration via the "config" attribute');if(!this.config.columns)throw new Error("[xosTable] Please provide a columns list in the configuration");this.config.order&&angular.isObject(this.config.order)&&(this.reverse=this.config.order.reverse||!1,this.orderBy=this.config.order.field||"id");var t=e.filter(this.config.columns,{type:"custom"});angular.isArray(t)&&t.length>0&&e.forEach(t,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided a custom field type, a formatter function should provided too.")});var r=e.filter(this.config.columns,{type:"icon"});angular.isArray(r)&&r.length>0&&e.forEach(r,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided an icon field type, a formatter function should provided too.")});var a=e.filter(this.config.columns,function(e){return angular.isDefined(e.link)});angular.isArray(a)&&a.length>0&&e.forEach(a,function(e){if(!angular.isFunction(e.link))throw new Error("[xosTable] The link property should be a function.")}),this.columns=this.config.columns,this.classes=this.config.classes||"table table-striped table-bordered",this.config.actions,this.config.pagination&&(this.currentPage=0,this.goToPage=function(e){o.currentPage=e})}]}).filter("arrayToList",function(){return function(e){return angular.isArray(e)?e.join(", "):e}}).directive("xosLinkWrapper",function(){return{restrict:"A",transclude:!0,template:'\n          <a ng-if="col.link" href="{{col.link(item)}}">\n            <div ng-transclude></div>\n          </a>\n          <div ng-transclude ng-if="!col.link"></div>\n        '}})}(),function(){angular.module("xos.uiComponents").component("xosPagination",{restrict:"E",bindings:{pageSize:"=",totalElements:"=",change:"="},template:'\n      <div class="row" ng-if="vm.pageList.length > 1">\n        <div class="col-xs-12 text-center">\n          <ul class="pagination">\n            <li\n              ng-click="vm.goToPage(vm.currentPage - 1)"\n              ng-class="{disabled: vm.currentPage == 0}">\n              <a href="" aria-label="Previous">\n                  <span aria-hidden="true">&laquo;</span>\n              </a>\n            </li>\n            <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n              <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n            </li>\n            <li\n              ng-click="vm.goToPage(vm.currentPage + 1)"\n              ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n              <a href="" aria-label="Next">\n                  <span aria-hidden="true">&raquo;</span>\n              </a>\n            </li>\n          </ul>\n        </div>\n      </div>\n    ',bindToController:!0,controllerAs:"vm",controller:["$scope",function(e){var n=this;this.currentPage=0,this.goToPage=function(e){e<0||e===n.pages||(n.currentPage=e,n.change(e))},this.createPages=function(e){for(var n=[],i=0;i<e;i++)n.push(i);return n},e.$watch(function(){return n.totalElements},function(){n.totalElements&&(n.pages=Math.ceil(n.totalElements/n.pageSize),n.pageList=n.createPages(n.pages))})}]}).filter("pagination",function(){return function(e,n){return e&&angular.isArray(e)?(n=parseInt(n,10),e.slice(n)):e}})}(),function(){angular.module("xos.uiComponents").component("xosForm",{restrict:"E",bindings:{config:"=",ngModel:"="},template:'\n      <form name="vm.{{vm.config.formName || \'form\'}}" novalidate>\n        <div class="form-group" ng-repeat="(name, field) in vm.formField">\n          <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>\n          <xos-validation field="vm[vm.config.formName || \'form\'][name]" form = "vm[vm.config.formName || \'form\']"></xos-validation>\n          <div class="alert alert-info" ng-show="(field.hint).length >0" role="alert">{{field.hint}}</div>\n        </div>\n        <div class="form-group" ng-if="vm.config.actions">\n        <xos-alert config="vm.config.feedback" show="vm.config.feedback.show">{{vm.config.feedback.message}}</xos-alert>\n\n          <button role="button" href=""\n            ng-repeat="action in vm.config.actions"\n            ng-click="action.cb(vm.ngModel, vm[vm.config.formName || \'form\'])"\n            class="btn btn-{{action.class}}"\n            title="{{action.label}}">\n            <i class="glyphicon glyphicon-{{action.icon}}"></i>\n            {{action.label}}\n          </button>\n        </div>\n      </form>\n    ',bindToController:!0,controllerAs:"vm",controller:["$scope","$log","_","XosFormHelpers",function(e,n,i,o){var t=this;if(!this.config)throw new Error('[xosForm] Please provide a configuration via the "config" attribute');if(!this.config.actions)throw new Error("[xosForm] Please provide an action list in the configuration");this.config.feedback||(this.config.feedback={show:!1,message:"Form submitted successfully !!!",type:"success"}),this.excludedField=["id","validators","created","updated","deleted","backend_status"],this.config&&this.config.exclude&&(this.excludedField=this.excludedField.concat(this.config.exclude)),this.formField=[],e.$watch(function(){return t.config},function(){if(t.ngModel){var e=i.difference(Object.keys(t.ngModel),t.excludedField),n=o.parseModelField(e);t.formField=o.buildFormStructure(n,t.config.fields,t.ngModel)}},!0),e.$watch(function(){return t.ngModel},function(e){if(t.formField={},e){var n=i.difference(Object.keys(e),t.excludedField),r=o.parseModelField(n);t.formField=o.buildFormStructure(r,t.config.fields,e,t.config.order)}})}]})}(),function(){angular.module("xos.uiComponents").component("xosField",{restrict:"E",bindings:{name:"=",field:"=",ngModel:"="},template:'\n      <label ng-if="vm.field.type !== \'object\' && vm.field.type !== \'array\'">{{vm.field.label}}</label>\n      <input\n        xos-custom-validator custom-validator="vm.field.validators.custom || null"\n        ng-if="vm.field.type !== \'boolean\' && vm.field.type !== \'object\' && vm.field.type !== \'select\' && vm.field.type !== \'array\'"\n        type="{{vm.field.type}}"\n        name="{{vm.name}}"\n        class="form-control"\n        ng-model="vm.ngModel"\n        ng-minlength="vm.field.validators.minlength || 0"\n        ng-maxlength="vm.field.validators.maxlength || 2000"\n        ng-required="vm.field.validators.required || false" />\n        <select class="form-control" ng-if ="vm.field.type === \'select\'"\n          name = "{{vm.name}}"\n          ng-options="item.id as item.label for item in vm.field.options"\n          ng-model="vm.ngModel"\n          ng-required="vm.field.validators.required || false">\n          </select>\n      <span class="boolean-field" ng-if="vm.field.type === \'boolean\'">\n        <a href="#"\n          class="btn btn-success"\n          ng-show="vm.ngModel"\n          ng-click="vm.ngModel = false">\n          <i class="glyphicon glyphicon-ok"></i>\n        </a>\n        <a href="#"\n          class="btn btn-danger"\n          ng-show="!vm.ngModel"\n          ng-click="vm.ngModel = true">\n          <i class="glyphicon glyphicon-remove"></i>\n        </a>\n      </span>\n      <div\n        class="panel panel-default object-field"\n        ng-if="vm.field.type == \'object\' && (!vm.isEmptyObject(vm.ngModel) || !vm.isEmptyObject(vm.field.properties))"\n        >\n        <div class="panel-heading">{{vm.field.label}}</div>\n        <div class="panel-body">\n          <div ng-if="!vm.field.properties" ng-repeat="(k, v) in vm.ngModel">\n            <xos-field\n              name="k"\n              field="{label: vm.formatLabel(k), type: vm.getType(v)}"\n              ng-model="v">\n            </xos-field>\n          </div>\n          <div ng-if="vm.field.properties" ng-repeat="(k, v) in vm.field.properties">\n            <xos-field\n              name="k"\n              field="{\n                label: v.label || vm.formatLabel(k),\n                type: v.type,\n                validators: v.validators\n              }"\n              ng-model="vm.ngModel[k]">\n            </xos-field>\n          </div>\n        </div>\n      </div>\n      <div\n        class="panel panel-default array-field"\n        ng-if="vm.field.type == \'array\'">\n        <div class="panel-heading">{{vm.field.label}}</div>\n        <div class="panel-body selected">\n          <ul class="draggable" dnd-list="vm.ngModel">\n            <li\n              class="array-element"\n              ng-repeat="item in vm.ngModel"\n              dnd-draggable="item"\n              dnd-moved="vm.ngModel.splice($index, 1)"\n              dnd-effect-allowed="move"\n              dnd-selected="models.selected = item"\n              >\n              <div class="well well-sm text-center">\n                {{item}}\n              </div>\n            </li>\n            <div class="clearfix"></div>\n          </ul>\n        </div>\n        <div class="panel-body unselected">\n          <ul class="draggable" dnd-list="vm.field.availableOptions">\n            <li\n              class="array-element"\n              ng-repeat="item in vm.field.availableOptions"\n              dnd-draggable="item"\n              dnd-moved="vm.field.availableOptions.splice($index, 1)"\n              dnd-effect-allowed="move"\n              dnd-selected="models.selected = item"\n              >\n              <div class="well well-sm text-center">\n                {{item}}\n              </div>\n            </li>\n            <div class="clearfix"></div>\n          </ul>\n        </div>\n      </div>\n    ',bindToController:!0,controllerAs:"vm",controller:["$attrs","$scope","XosFormHelpers","LabelFormatter","_",function(e,n,i,o,t){var r=this;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.field.type)throw new Error("[xosField] Please provide a type in the field definition");if(!e.ngModel)throw new Error("[xosField] Please provide an ng-model");this.getType=i._getFieldFormat,this.formatLabel=o.format,this.isEmptyObject=function(e){return!e||0===Object.keys(e).length},"array"===this.field.type&&n.$watch(function(){return r.ngModel.length},function(){r.field.availableOptions=t.difference(r.field.options,r.ngModel)})}]}).directive("xosCustomValidator",function(){return{restrict:"A",scope:{fn:"=customValidator"},require:"ngModel",link:function(e,n,i,o){function t(n){var i=e.fn(n);return angular.isArray(i)?o.$setValidity.apply(o,_toConsumableArray(i)):o.$setValidity("custom",i),n}angular.isFunction(e.fn)&&o.$parsers.push(t)}}})}(),function(){angular.module("xos.uiComponents").component("xosAlert",{restrict:"E",bindings:{config:"=",show:"=?"},template:'\n      <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n        <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n          <span aria-hidden="true">&times;</span>\n        </button>\n        <p ng-transclude></p>\n      </div>\n    ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$timeout",function(e){var n=this;if(!this.config)throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');this.show=this.show!==!1,this.dismiss=function(){n.show=!1},this.config.autoHide&&!function(){var i=e(function(){n.dismiss(),e.cancel(i)},n.config.autoHide)}()}]})}(),function(){function e(){var e=function(e){return e.split("_").join(" ").trim()},n=function(e){return e.split(/(?=[A-Z])/).map(function(e){return e.toLowerCase()}).join(" ")},i=function(e){return e.slice(0,1).toUpperCase()+e.slice(1)},o=function(o){return o=e(o),o=n(o),o=i(o).replace(/\s\s+/g," ")+":",o.replace("::",":")};return{_formatByUnderscore:e,_formatByUppercase:n,_capitalize:i,format:o}}angular.module("xos.uiComponents").factory("LabelFormatter",e)}();var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(){angular.module("xos.uiComponents").service("XosFormHelpers",["_","LabelFormatter",function(e,n){var i=this;this._isEmail=function(e){var n=/(([^<>()[\]\\.,;:\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 n.test(e)},this._getFieldFormat=function(e){return angular.isArray(e)?"array":angular.isDate(e)||!Number.isNaN(Date.parse(e))&&/^\d+-\d+-\d+\D\d+:\d+:\d+\.\d+\D/.test(e)?"date":"boolean"==typeof e?"boolean":i._isEmail(e)?"email":angular.isString(e)||null===e?"text":"undefined"==typeof e?"undefined":_typeof(e)},this.buildFormStructure=function(o,t,r,a){var s={};return o=angular.extend(o,t),t=t||{},a&&e.each(a,function(e){s[e]={}}),e.each(Object.keys(o),function(e){s[e]={label:t[e]&&t[e].label?t[e].label+":":n.format(e),type:t[e]&&t[e].type?t[e].type:i._getFieldFormat(r[e]),validators:t[e]&&t[e].validators?t[e].validators:{},hint:t[e]&&t[e].hint?t[e].hint:""},t[e]&&t[e].options&&(s[e].options=t[e].options),t[e]&&t[e].properties&&(s[e].properties=t[e].properties),"date"===s[e].type&&(r[e]=new Date(r[e])),"number"===s[e].type&&(r[e]=parseInt(r[e],10))}),s},this.parseModelField=function(n){return e.reduce(n,function(e,n){return e[n]={},e},{})}}])}(),function(){function e(e){return function(n,i){if(angular.isUndefined(n))return!1;if(null===n||null===i)return n===i;if(angular.isObject(i)||angular.isObject(n))return angular.equals(i,n);if(e.isBoolean(n)||e.isBoolean(i))return 0!==n&&1!==n||(n=!!n),angular.equals(i,n);if(!angular.isString(n)||!angular.isString(i)){if(!angular.isDefined(n.toString)||!angular.isDefined(i.toString))return n===i;n=n.toString(),i=i.toString()}return n=n.toLowerCase()+"",i=i.toLowerCase()+"",n.indexOf(i)!==-1}}e.$inject=["_"],angular.module("xos.uiComponents").factory("Comparator",e)}(),function(){function e(e,n,i){e.interceptors.push("SetCSRFToken"),i.defaults.stripTrailingSlashes=!1}e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"],angular.module("xos.helpers",["ngCookies","ngResource","ngAnimate","xos.uiComponents"]).config(e).factory("_",["$window",function(e){return e._}])}(),function(){angular.module("xos.helpers").service("vSG-Collection",["$resource",function(e){return e("/api/service/vsg/")}])}(),function(){angular.module("xos.helpers").service("vOLT-Collection",["$resource",function(e){return e("/api/tenant/cord/volt/:volt_id/",{volt_id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Login",["$resource",function(e){return e("/api/utility/login/")}]).service("Logout",["$resource",function(e){return e("/api/utility/logout/")}])}(),function(){angular.module("xos.helpers").service("Users",["$resource",function(e){return e("/api/core/users/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Truckroll",["$resource",function(e){return e("/api/tenant/truckroll/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Tenants",["$resource",function(e){return e("/api/core/tenants/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Subscribers",["$resource",function(e){return e("/api/tenant/cord/subscriber/:id/",{id:"@id"},{update:{method:"PUT"},"View-a-Subscriber-Features-Detail":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/"},"Read-Subscriber-uplink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Update-Subscriber-uplink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Read-Subscriber-downlink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Update-Subscriber-downlink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Read-Subscriber-cdn":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Update-Subscriber-cdn":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Read-Subscriber-uverse":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Update-Subscriber-uverse":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Read-Subscriber-status":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"},"Update-Subscriber-status":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"}})}])}(),function(){angular.module("xos.helpers").service("SlicesPlus",["$http","$q",function(e,n){this.query=function(i){var o=n.defer();return e.get("/api/utility/slicesplus/",{params:i}).then(function(e){o.resolve(e.data)})["catch"](function(e){o.reject(e.data)}),{$promise:o.promise}},this.get=function(i,o){var t=n.defer();return e.get("/api/utility/slicesplus/"+i,{params:o}).then(function(e){t.resolve(e.data)})["catch"](function(e){t.reject(e.data)}),{$promise:t.promise}}}])}(),function(){angular.module("xos.helpers").service("Slices",["$resource",function(e){return e("/api/core/slices/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Sites",["$resource",function(e){return e("/api/core/sites/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Services",["$resource",function(e){return e("/api/core/services/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("ONOS-Services-Collection",["$resource",function(e){return e("/api/service/onos/")}])}(),function(){angular.module("xos.helpers").service("ONOS-App-Collection",["$resource",function(e){return e("/api/tenant/onos/app/")}])}(),function(){angular.module("xos.helpers").service("Nodes",["$resource",function(e){return e("/api/core/nodes/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Networkstemplates",["$resource",function(e){return e("/api/core/networktemplates/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Networks",["$resource",function(e){return e("/api/core/networks/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Me",["$q","$http",function(e,n){this.get=function(){var i=e.defer();return n.get("/api/utility/me/").then(function(e){i.resolve(e.data)})["catch"](function(e){i.reject(e)}),i.promise}}])}(),function(){angular.module("xos.helpers").service("Instances",["$resource",function(e){return e("/api/core/instances/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Images",["$resource",function(e){return e("/api/core/images/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Flavors",["$resource",function(e){return e("/api/core/flavors/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Example-Services-Collection",["$resource",function(e){return e("/api/service/exampleservice/")}])}(),function(){angular.module("xos.helpers").service("Deployments",["$resource",function(e){return e("/api/core/deployments/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Dashboards",["$resource","$q","$http",function(e,n,i){var o=e("/api/core/dashboardviews/:id/",{id:"@id"},{update:{method:"PUT"}});return o.prototype.$save=function(){var e=n.defer();return i.put("/api/core/dashboardviews/"+this.id+"/",this).then(function(n){e.resolve(n.data)})["catch"](function(n){e.reject(n.data)}),e.promise},o}])}(),function(){angular.module("xos.helpers").service("XosUserPrefs",["$cookies","Me","$q",function(e,n,i){var o=this,t=e.get("xosUserPrefs")?angular.fromJson(e.get("xosUserPrefs")):{};this.getAll=function(){return t=e.get("xosUserPrefs")?angular.fromJson(e.get("xosUserPrefs")):{}},this.setAll=function(n){e.put("xosUserPrefs",angular.toJson(n))},this.getSynchronizerNotificationStatus=function(){var e=!(arguments.length<=0||void 0===arguments[0])&&arguments[0];return e?o.getAll().synchronizers.notification[e]:o.getAll().synchronizers.notification},this.getUserDetailsCookie=function(){
+"use strict";function _toConsumableArray(e){if(Array.isArray(e)){for(var n=0,i=Array(e.length);n<e.length;n++)i[n]=e[n];return i}return Array.from(e)}!function(){angular.module("xos.uiComponents",["chart.js","RecursionHelper","dndLists"])}(),function(){angular.module("xos.uiComponents").component("xosSmartTable",{restrict:"E",bindings:{config:"="},template:'\n        <div class="row" ng-show="vm.data.length > 0">\n          <div class="col-xs-12 text-right">\n            <a href="" class="btn btn-success" ng-click="vm.createItem()">\n              Add\n            </a>\n          </div>\n        </div>\n        <div class="row">\n          <div class="col-xs-12 table-responsive">\n            <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n          </div>\n        </div>\n        <div class="panel panel-default" ng-show="vm.detailedItem">\n          <div class="panel-heading">\n            <div class="row">\n              <div class="col-xs-11">\n                <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n                <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n              </div>\n              <div class="col-xs-1">\n                <a href="" ng-click="vm.cleanForm()">\n                  <i class="glyphicon glyphicon-remove pull-right"></i>\n                </a>\n              </div>\n            </div>\n          </div>\n          <div class="panel-body">\n            <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n          </div>\n        </div>\n        <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n        <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n      ',bindToController:!0,controllerAs:"vm",controller:["$injector","LabelFormatter","_","XosFormHelpers",function(e,n,i,o){var t=this;this.responseMsg=!1,this.responseErr=!1,this.tableConfig={columns:[],actions:[{label:"delete",icon:"remove",cb:function(e){t.Resource["delete"]({id:e.id}).$promise.then(function(){i.remove(t.data,function(n){return n.id===e.id}),t.responseMsg=t.config.resource+" with id "+e.id+" successfully deleted"})["catch"](function(n){t.responseErr=n.data.detail||"Error while deleting "+t.config.resource+" with id "+e.id})},color:"red"},{label:"details",icon:"search",cb:function(e){t.detailedItem=e}}],classes:"table table-striped table-bordered table-responsive",filter:"field",order:!0,pagination:{pageSize:10}},this.formConfig={exclude:this.config.hiddenFields,fields:{},formName:this.config.resource+"Form",actions:[{label:"Save",icon:"ok",cb:function(e){var n=void 0,i=!0;e.id?(n=e.$update(),i=!1):n=e.$save(),n.then(function(n){i&&t.data.push(angular.copy(n)),delete t.detailedItem,t.responseMsg=t.config.resource+" with id "+e.id+" successfully saved"})["catch"](function(n){t.responseErr=n.data.detail||"Error while saving "+t.config.resource+" with id "+e.id})},"class":"success"}]},this.cleanForm=function(){delete t.detailedItem},this.createItem=function(){t.detailedItem=new t.Resource},this.Resource=e.get(this.config.resource);var r=function(){t.Resource.query().$promise.then(function(e){if(!e[0])return void(t.data=e);var r=e[0],a=Object.keys(r);i.remove(a,function(e){return"id"===e||"validators"===e}),angular.isArray(t.config.hiddenFields)&&(a=i.difference(a,t.config.hiddenFields)),a.forEach(function(e){var i={label:n.format(e),prop:e};i.type=o._getFieldFormat(r[e]),t.tableConfig.columns.push(i)}),a.forEach(function(e){t.formConfig.fields[e]={label:n.format(e).replace(":",""),type:o._getFieldFormat(r[e])}}),t.data=e})};r()}]})}(),function(){angular.module("xos.uiComponents").component("xosSmartPie",{restrict:"E",bindings:{config:"="},template:'\n      <canvas\n        class="chart chart-pie {{vm.config.classes}}"\n        chart-data="vm.data" chart-labels="vm.labels"\n        chart-legend="{{vm.config.legend}}">\n      </canvas>\n    ',bindToController:!0,controllerAs:"vm",controller:["$injector","$interval","$scope","$timeout","_",function(e,n,i,o,t){var r=this;if(!this.config.resource&&!this.config.data)throw new Error("[xosSmartPie] Please provide a resource or an array of data in the configuration");var a=function(e){return t.groupBy(e,r.config.groupBy)},s=function(e){return t.reduce(Object.keys(e),function(n,i){return n.concat(e[i].length)},[])},l=function(e){return angular.isFunction(r.config.labelFormatter)?r.config.labelFormatter(Object.keys(e)):Object.keys(e)},c=function(e){var n=a(e);r.data=s(n),r.labels=l(n)};this.config.resource?!function(){r.Resource=e.get(r.config.resource);var i=function(){r.Resource.query().$promise.then(function(e){e[0]&&c(e)})};i(),r.config.poll&&n(function(){i()},1e3*r.config.poll)}():i.$watch(function(){return r.config.data},function(e){e&&c(r.config.data)},!0),i.$on("create",function(e,n){console.log("create: "+n.id)}),i.$on("destroy",function(e,n){console.log("destroy: "+n.id)})}]})}(),function(){angular.module("xos.uiComponents").component("xosValidation",{restrict:"E",bindings:{field:"=",form:"="},template:'\n      <div ng-cloak>\n        <xos-alert config="vm.config" show="vm.field.$error.required !== undefined && vm.field.$error.required !== false  && (vm.field.$touched || vm.form.$submitted)">\n          Field required\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">\n          This is not a valid email\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">\n          Too short\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">\n          Too long\n        </xos-alert>\n        <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">\n          Field invalid\n        </xos-alert>\n      </div>\n    ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:function(){this.config={type:"danger"}}})}(),function(){angular.module("xos.uiComponents").component("xosTable",{restrict:"E",bindings:{data:"=",config:"="},template:'\n        <div ng-show="vm.data.length > 0 && vm.loader == false">\n          <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n            <div class="col-xs-12">\n              <input\n                class="form-control"\n                placeholder="Type to search.."\n                type="text"\n                ng-model="vm.query"/>\n            </div>\n          </div>\n          <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n            <thead>\n              <tr>\n                <th ng-repeat="col in vm.columns">\n                  {{col.label}}\n                  <span ng-if="vm.config.order">\n                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n                      <i class="glyphicon glyphicon-chevron-up"></i>\n                    </a>\n                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n                      <i class="glyphicon glyphicon-chevron-down"></i>\n                    </a>\n                  </span>\n                </th>\n                <th ng-if="vm.config.actions">Actions:</th>\n              </tr>\n            </thead>\n            <tbody ng-if="vm.config.filter == \'field\'">\n              <tr>\n                <td ng-repeat="col in vm.columns">\n                  <input\n                    ng-if="col.type !== \'boolean\' && col.type !== \'array\' && col.type !== \'object\' && col.type !== \'custom\'"\n                    class="form-control"\n                    placeholder="Type to search by {{col.label}}"\n                    type="text"\n                    ng-model="vm.query[col.prop]"/>\n                  <select\n                    ng-if="col.type === \'boolean\'"\n                    class="form-control"\n                    ng-model="vm.query[col.prop]">\n                    <option value="">-</option>\n                    <option value="true">True</option>\n                    <option value="false">False</option>\n                  </select>\n                </td>\n                <td ng-if="vm.config.actions"></td>\n              </tr>\n            </tbody>\n            <tbody>\n              <tr ng-repeat="item in vm.data | filter:vm.query:vm.comparator | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n                <td ng-repeat="col in vm.columns" xos-link-wrapper>\n                  <span ng-if="!col.type || col.type === \'text\'">{{item[col.prop]}}</span>\n                  <span ng-if="col.type === \'boolean\'">\n                    <i class="glyphicon"\n                      ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n                    </i>\n                  </span>\n                  <span ng-if="col.type === \'date\'">\n                    {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n                  </span>\n                  <span ng-if="col.type === \'array\'">\n                    {{item[col.prop] | arrayToList}}\n                  </span>\n                  <span ng-if="col.type === \'object\'">\n                    <dl class="dl-horizontal">\n                      <span ng-repeat="(k,v) in item[col.prop]">\n                        <dt>{{k}}</dt>\n                        <dd>{{v}}</dd>\n                      </span>\n                    </dl>\n                  </span>\n                  <span ng-if="col.type === \'custom\'">\n                    {{col.formatter(item)}}\n                  </span>\n                  <span ng-if="col.type === \'icon\'">\n                    <i class="glyphicon glyphicon-{{col.formatter(item)}}">\n                    </i>\n                  </span>\n                </td>\n                <td ng-if="vm.config.actions">\n                  <a href=""\n                    ng-repeat="action in vm.config.actions"\n                    ng-click="action.cb(item)"\n                    title="{{action.label}}">\n                    <i\n                      class="glyphicon glyphicon-{{action.icon}}"\n                      style="color: {{action.color}};"></i>\n                  </a>\n                </td>\n              </tr>\n            </tbody>\n          </table>\n          <xos-pagination\n            ng-if="vm.config.pagination"\n            page-size="vm.config.pagination.pageSize"\n            total-elements="vm.data.length"\n            change="vm.goToPage">\n            </xos-pagination>\n        </div>\n        <div ng-show="(vm.data.length == 0 || !vm.data) && vm.loader == false">\n           <xos-alert config="{type: \'info\'}">\n            No data to show.\n          </xos-alert>\n        </div>\n        <div ng-show="vm.loader == true">\n          <div class="loader"></div>\n        </div>\n      ',bindToController:!0,controllerAs:"vm",controller:["_","$scope","Comparator",function(e,n,i){var o=this;if(this.comparator=i,this.loader=!0,n.$watch(function(){return o.data},function(e){angular.isDefined(e)&&(o.loader=!1)}),!this.config)throw new Error('[xosTable] Please provide a configuration via the "config" attribute');if(!this.config.columns)throw new Error("[xosTable] Please provide a columns list in the configuration");this.config.order&&angular.isObject(this.config.order)&&(this.reverse=this.config.order.reverse||!1,this.orderBy=this.config.order.field||"id");var t=e.filter(this.config.columns,{type:"custom"});angular.isArray(t)&&t.length>0&&e.forEach(t,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided a custom field type, a formatter function should provided too.")});var r=e.filter(this.config.columns,{type:"icon"});angular.isArray(r)&&r.length>0&&e.forEach(r,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided an icon field type, a formatter function should provided too.")});var a=e.filter(this.config.columns,function(e){return angular.isDefined(e.link)});angular.isArray(a)&&a.length>0&&e.forEach(a,function(e){if(!angular.isFunction(e.link))throw new Error("[xosTable] The link property should be a function.")}),this.columns=this.config.columns,this.classes=this.config.classes||"table table-striped table-bordered",this.config.actions,this.config.pagination&&(this.currentPage=0,this.goToPage=function(e){o.currentPage=e})}]}).filter("arrayToList",function(){return function(e){return angular.isArray(e)?e.join(", "):e}}).directive("xosLinkWrapper",function(){return{restrict:"A",transclude:!0,template:'\n          <a ng-if="col.link" href="{{col.link(item)}}">\n            <div ng-transclude></div>\n          </a>\n          <div ng-transclude ng-if="!col.link"></div>\n        '}})}(),function(){angular.module("xos.uiComponents").component("xosPagination",{restrict:"E",bindings:{pageSize:"=",totalElements:"=",change:"="},template:'\n      <div class="row" ng-if="vm.pageList.length > 1">\n        <div class="col-xs-12 text-center">\n          <ul class="pagination">\n            <li\n              ng-click="vm.goToPage(vm.currentPage - 1)"\n              ng-class="{disabled: vm.currentPage == 0}">\n              <a href="" aria-label="Previous">\n                  <span aria-hidden="true">&laquo;</span>\n              </a>\n            </li>\n            <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n              <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n            </li>\n            <li\n              ng-click="vm.goToPage(vm.currentPage + 1)"\n              ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n              <a href="" aria-label="Next">\n                  <span aria-hidden="true">&raquo;</span>\n              </a>\n            </li>\n          </ul>\n        </div>\n      </div>\n    ',bindToController:!0,controllerAs:"vm",controller:["$scope",function(e){var n=this;this.currentPage=0,this.goToPage=function(e){e<0||e===n.pages||(n.currentPage=e,n.change(e))},this.createPages=function(e){for(var n=[],i=0;i<e;i++)n.push(i);return n},e.$watch(function(){return n.totalElements},function(){n.totalElements&&(n.pages=Math.ceil(n.totalElements/n.pageSize),n.pageList=n.createPages(n.pages))})}]}).filter("pagination",function(){return function(e,n){return e&&angular.isArray(e)?(n=parseInt(n,10),e.slice(n)):e}})}(),function(){angular.module("xos.uiComponents").component("xosField",{restrict:"E",bindings:{name:"=",field:"=",ngModel:"="},template:'\n      <label ng-if="vm.field.type !== \'object\' && vm.field.type !== \'array\'">{{vm.field.label}}</label>\n      <input\n        xos-custom-validator custom-validator="vm.field.validators.custom || null"\n        ng-if="vm.field.type !== \'boolean\' && vm.field.type !== \'object\' && vm.field.type !== \'select\' && vm.field.type !== \'array\'"\n        type="{{vm.field.type}}"\n        name="{{vm.name}}"\n        class="form-control"\n        ng-model="vm.ngModel"\n        ng-minlength="vm.field.validators.minlength || 0"\n        ng-maxlength="vm.field.validators.maxlength || 2000"\n        ng-required="vm.field.validators.required || false" />\n        <select class="form-control" ng-if ="vm.field.type === \'select\'"\n          name = "{{vm.name}}"\n          ng-options="item.id as item.label for item in vm.field.options"\n          ng-model="vm.ngModel"\n          ng-required="vm.field.validators.required || false">\n          </select>\n      <span class="boolean-field" ng-if="vm.field.type === \'boolean\'">\n        <a href="#"\n          class="btn btn-success"\n          ng-show="vm.ngModel"\n          ng-click="vm.ngModel = false">\n          <i class="glyphicon glyphicon-ok"></i>\n        </a>\n        <a href="#"\n          class="btn btn-danger"\n          ng-show="!vm.ngModel"\n          ng-click="vm.ngModel = true">\n          <i class="glyphicon glyphicon-remove"></i>\n        </a>\n      </span>\n      <div\n        class="panel panel-default object-field"\n        ng-if="vm.field.type == \'object\' && (!vm.isEmptyObject(vm.ngModel) || !vm.isEmptyObject(vm.field.properties))"\n        >\n        <div class="panel-heading">{{vm.field.label}}</div>\n        <div class="panel-body">\n          <div ng-if="!vm.field.properties" ng-repeat="(k, v) in vm.ngModel">\n            <xos-field\n              name="k"\n              field="{label: vm.formatLabel(k), type: vm.getType(v)}"\n              ng-model="v">\n            </xos-field>\n          </div>\n          <div ng-if="vm.field.properties" ng-repeat="(k, v) in vm.field.properties">\n            <xos-field\n              name="k"\n              field="{\n                label: v.label || vm.formatLabel(k),\n                type: v.type,\n                validators: v.validators\n              }"\n              ng-model="vm.ngModel[k]">\n            </xos-field>\n          </div>\n        </div>\n      </div>\n      <div\n        class="panel panel-default array-field"\n        ng-if="vm.field.type == \'array\'">\n        <div class="panel-heading">{{vm.field.label}}</div>\n        <div class="panel-body selected">\n          <ul class="draggable" dnd-list="vm.ngModel">\n            <li\n              class="array-element"\n              ng-repeat="item in vm.ngModel"\n              dnd-draggable="item"\n              dnd-moved="vm.ngModel.splice($index, 1)"\n              dnd-effect-allowed="move"\n              dnd-selected="models.selected = item"\n              >\n              <div class="well well-sm text-center">\n                {{item}}\n              </div>\n            </li>\n            <div class="clearfix"></div>\n          </ul>\n        </div>\n        <div class="panel-body unselected">\n          <ul class="draggable" dnd-list="vm.field.availableOptions">\n            <li\n              class="array-element"\n              ng-repeat="item in vm.field.availableOptions"\n              dnd-draggable="item"\n              dnd-moved="vm.field.availableOptions.splice($index, 1)"\n              dnd-effect-allowed="move"\n              dnd-selected="models.selected = item"\n              >\n              <div class="well well-sm text-center">\n                {{item}}\n              </div>\n            </li>\n            <div class="clearfix"></div>\n          </ul>\n        </div>\n      </div>\n    ',bindToController:!0,controllerAs:"vm",controller:["$attrs","$scope","XosFormHelpers","LabelFormatter","_",function(e,n,i,o,t){var r=this;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.field.type)throw new Error("[xosField] Please provide a type in the field definition");if(!e.ngModel)throw new Error("[xosField] Please provide an ng-model");this.getType=i._getFieldFormat,this.formatLabel=o.format,this.isEmptyObject=function(e){return!e||0===Object.keys(e).length},"array"===this.field.type&&n.$watch(function(){return r.ngModel.length},function(){r.field.availableOptions=t.difference(r.field.options,r.ngModel)})}]}).directive("xosCustomValidator",function(){return{restrict:"A",scope:{fn:"=customValidator"},require:"ngModel",link:function(e,n,i,o){function t(n){var i=e.fn(n);return angular.isArray(i)?o.$setValidity.apply(o,_toConsumableArray(i)):o.$setValidity("custom",i),n}angular.isFunction(e.fn)&&o.$parsers.push(t)}}})}(),function(){angular.module("xos.uiComponents").component("xosForm",{restrict:"E",bindings:{config:"=",ngModel:"="},template:'\n      <form name="vm.{{vm.config.formName || \'form\'}}" novalidate>\n        <div class="form-group" ng-repeat="(name, field) in vm.formField">\n          <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>\n          <xos-validation field="vm[vm.config.formName || \'form\'][name]" form = "vm[vm.config.formName || \'form\']"></xos-validation>\n          <div class="alert alert-info" ng-show="(field.hint).length >0" role="alert">{{field.hint}}</div>\n        </div>\n        <div class="form-group" ng-if="vm.config.actions">\n        <xos-alert config="vm.config.feedback" show="vm.config.feedback.show">{{vm.config.feedback.message}}</xos-alert>\n\n          <button role="button" href=""\n            ng-repeat="action in vm.config.actions"\n            ng-click="action.cb(vm.ngModel, vm[vm.config.formName || \'form\'])"\n            class="btn btn-{{action.class}}"\n            title="{{action.label}}">\n            <i class="glyphicon glyphicon-{{action.icon}}"></i>\n            {{action.label}}\n          </button>\n        </div>\n      </form>\n    ',bindToController:!0,controllerAs:"vm",controller:["$scope","$log","_","XosFormHelpers",function(e,n,i,o){var t=this;if(!this.config)throw new Error('[xosForm] Please provide a configuration via the "config" attribute');if(!this.config.actions)throw new Error("[xosForm] Please provide an action list in the configuration");this.config.feedback||(this.config.feedback={show:!1,message:"Form submitted successfully !!!",type:"success"}),this.excludedField=["id","validators","created","updated","deleted","backend_status"],this.config&&this.config.exclude&&(this.excludedField=this.excludedField.concat(this.config.exclude)),this.formField=[],e.$watch(function(){return t.config},function(){if(t.ngModel){var e=i.difference(Object.keys(t.ngModel),t.excludedField),n=o.parseModelField(e);t.formField=o.buildFormStructure(n,t.config.fields,t.ngModel,t.config.order)}},!0),e.$watch(function(){return t.ngModel},function(e){if(t.formField={},e){var n=i.difference(Object.keys(e),t.excludedField),r=o.parseModelField(n);t.formField=o.buildFormStructure(r,t.config.fields,e,t.config.order)}})}]})}(),function(){angular.module("xos.uiComponents").component("xosAlert",{restrict:"E",bindings:{config:"=",show:"=?"},template:'\n      <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n        <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n          <span aria-hidden="true">&times;</span>\n        </button>\n        <p ng-transclude></p>\n      </div>\n    ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$timeout",function(e){var n=this;if(!this.config)throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');this.show=this.show!==!1,this.dismiss=function(){n.show=!1},this.config.autoHide&&!function(){var i=e(function(){n.dismiss(),e.cancel(i)},n.config.autoHide)}()}]})}(),function(){function e(){var e=function(e){return e.split("_").join(" ").trim()},n=function(e){return e.split(/(?=[A-Z])/).map(function(e){return e.toLowerCase()}).join(" ")},i=function(e){return e.slice(0,1).toUpperCase()+e.slice(1)},o=function(o){return o=e(o),o=n(o),o=i(o).replace(/\s\s+/g," ")+":",o.replace("::",":")};return{_formatByUnderscore:e,_formatByUppercase:n,_capitalize:i,format:o}}angular.module("xos.uiComponents").factory("LabelFormatter",e)}();var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(){angular.module("xos.uiComponents").service("XosFormHelpers",["_","LabelFormatter",function(e,n){var i=this;this._isEmail=function(e){var n=/(([^<>()[\]\\.,;:\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 n.test(e)},this._getFieldFormat=function(e){return angular.isArray(e)?"array":angular.isDate(e)||!Number.isNaN(Date.parse(e))&&/^\d+-\d+-\d+\D\d+:\d+:\d+\.\d+\D/.test(e)?"date":"boolean"==typeof e?"boolean":i._isEmail(e)?"email":angular.isString(e)||null===e?"text":"undefined"==typeof e?"undefined":_typeof(e)},this.buildFormStructure=function(o,t,r,a){var s={};return o=angular.extend(o,t),t=t||{},a&&e.each(a,function(e){s[e]={}}),e.each(Object.keys(o),function(e){s[e]={label:t[e]&&t[e].label?t[e].label+":":n.format(e),type:t[e]&&t[e].type?t[e].type:i._getFieldFormat(r[e]),validators:t[e]&&t[e].validators?t[e].validators:{},hint:t[e]&&t[e].hint?t[e].hint:""},t[e]&&t[e].options&&(s[e].options=t[e].options),t[e]&&t[e].properties&&(s[e].properties=t[e].properties),"date"===s[e].type&&(r[e]=new Date(r[e])),"number"===s[e].type&&(r[e]=parseInt(r[e],10))}),s},this.parseModelField=function(n){return e.reduce(n,function(e,n){return e[n]={},e},{})}}])}(),function(){function e(e){return function(n,i){if(angular.isUndefined(n))return!1;if(null===n||null===i)return n===i;if(angular.isObject(i)||angular.isObject(n))return angular.equals(i,n);if(e.isBoolean(n)||e.isBoolean(i))return 0!==n&&1!==n||(n=!!n),angular.equals(i,n);if(!angular.isString(n)||!angular.isString(i)){if(!angular.isDefined(n.toString)||!angular.isDefined(i.toString))return n===i;n=n.toString(),i=i.toString()}return n=n.toLowerCase()+"",i=i.toLowerCase()+"",n.indexOf(i)!==-1}}e.$inject=["_"],angular.module("xos.uiComponents").factory("Comparator",e)}(),function(){function e(e,n,i){e.interceptors.push("SetCSRFToken"),i.defaults.stripTrailingSlashes=!1}e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"],angular.module("xos.helpers",["ngCookies","ngResource","ngAnimate","xos.uiComponents"]).config(e).factory("_",["$window",function(e){return e._}])}(),function(){angular.module("xos.helpers").service("vSG-Collection",["$resource",function(e){return e("/api/service/vsg/")}])}(),function(){angular.module("xos.helpers").service("vOLT-Collection",["$resource",function(e){return e("/api/tenant/cord/volt/:volt_id/",{volt_id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Login",["$resource",function(e){return e("/api/utility/login/")}]).service("Logout",["$resource",function(e){return e("/api/utility/logout/")}])}(),function(){angular.module("xos.helpers").service("Users",["$resource",function(e){return e("/api/core/users/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Truckroll",["$resource",function(e){return e("/api/tenant/truckroll/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Tenants",["$resource",function(e){return e("/api/core/tenants/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Subscribers",["$resource",function(e){return e("/api/tenant/cord/subscriber/:id/",{id:"@id"},{update:{method:"PUT"},"View-a-Subscriber-Features-Detail":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/"},"Read-Subscriber-uplink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Update-Subscriber-uplink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Read-Subscriber-downlink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Update-Subscriber-downlink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Read-Subscriber-cdn":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Update-Subscriber-cdn":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Read-Subscriber-uverse":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Update-Subscriber-uverse":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Read-Subscriber-status":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"},"Update-Subscriber-status":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"}})}])}(),function(){angular.module("xos.helpers").service("SlicesPlus",["$http","$q",function(e,n){this.query=function(i){var o=n.defer();return e.get("/api/utility/slicesplus/",{params:i}).then(function(e){o.resolve(e.data)})["catch"](function(e){o.reject(e.data)}),{$promise:o.promise}},this.get=function(i,o){var t=n.defer();return e.get("/api/utility/slicesplus/"+i,{params:o}).then(function(e){t.resolve(e.data)})["catch"](function(e){t.reject(e.data)}),{$promise:t.promise}}}])}(),function(){angular.module("xos.helpers").service("Slices",["$resource",function(e){return e("/api/core/slices/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Sites",["$resource",function(e){return e("/api/core/sites/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Services",["$resource",function(e){return e("/api/core/services/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("ONOS-Services-Collection",["$resource",function(e){return e("/api/service/onos/")}])}(),function(){angular.module("xos.helpers").service("ONOS-App-Collection",["$resource",function(e){return e("/api/tenant/onos/app/")}])}(),function(){angular.module("xos.helpers").service("Nodes",["$resource",function(e){return e("/api/core/nodes/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Networkstemplates",["$resource",function(e){return e("/api/core/networktemplates/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Networks",["$resource",function(e){return e("/api/core/networks/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Me",["$q","$http",function(e,n){this.get=function(){var i=e.defer();return n.get("/api/utility/me/").then(function(e){i.resolve(e.data)})["catch"](function(e){i.reject(e)}),i.promise}}])}(),function(){angular.module("xos.helpers").service("Instances",["$resource",function(e){return e("/api/core/instances/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Images",["$resource",function(e){return e("/api/core/images/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Flavors",["$resource",function(e){return e("/api/core/flavors/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Example-Services-Collection",["$resource",function(e){return e("/api/service/exampleservice/")}])}(),function(){angular.module("xos.helpers").service("Deployments",["$resource",function(e){return e("/api/core/deployments/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Dashboards",["$resource","$q","$http",function(e,n,i){var o=e("/api/core/dashboardviews/:id/",{id:"@id"},{update:{method:"PUT"}});return o.prototype.$save=function(){var e=n.defer();return i.put("/api/core/dashboardviews/"+this.id+"/",this).then(function(n){e.resolve(n.data)})["catch"](function(n){e.reject(n.data)}),e.promise},o}])}(),function(){angular.module("xos.helpers").service("XosUserPrefs",["$cookies","Me","$q",function(e,n,i){var o=this,t=e.get("xosUserPrefs")?angular.fromJson(e.get("xosUserPrefs")):{};this.getAll=function(){return t=e.get("xosUserPrefs")?angular.fromJson(e.get("xosUserPrefs")):{}},this.setAll=function(n){e.put("xosUserPrefs",angular.toJson(n))},this.getSynchronizerNotificationStatus=function(){var e=!(arguments.length<=0||void 0===arguments[0])&&arguments[0];return e?o.getAll().synchronizers.notification[e]:o.getAll().synchronizers.notification},this.getUserDetailsCookie=function(){
 var e=i.defer(),n=o.getAll();return n.userData?e.resolve(n.userData):o.setUserDetailsCookie().$promise.then(function(n){e.resolve(n)}),{$promise:e.promise}},this.setUserDetailsCookie=function(){var e=arguments.length<=0||void 0===arguments[0]?null:arguments[0],t=i.defer(),r=o.getAll();return e?(r.userData=e,o.setAll(r),t.resolve(e)):n.get().then(function(e){r.userData=e,o.setAll(r),t.resolve(e)})["catch"](function(e){t.reject(e)}),{$promise:t.promise}},this.setSynchronizerNotificationStatus=function(){var e=!(arguments.length<=0||void 0===arguments[0])&&arguments[0],n=arguments[1];if(!e)throw new Error("[XosUserPrefs] When updating a synchronizer is mandatory to provide a name.");var i=o.getAll();i.synchronizers||(i.synchronizers={notification:{}}),i.synchronizers.notification[e]=n,o.setAll(i)}}])}(),function(){angular.module("xos.helpers").service("GraphService",["$q","Tenants","Services",function(e,n,i){var o=this;this.loadCoarseData=function(){var o=void 0,t=e.defer();return i.query().$promise.then(function(e){return o=e,n.query({kind:"coarse"}).$promise}).then(function(e){t.resolve({tenants:e,services:o})}),t.promise},this.getCoarseGraph=function(){return o.loadCoarseData().then(function(e){console.log(e)}),"ciao"}}])}(),function(){angular.module("xos.helpers").factory("Notification",function(){return window.Notification}).service("xosNotification",["$q","$log","Notification",function(e,n,i){var o=this;this.checkPermission=function(){var n=e.defer();return i.requestPermission().then(function(e){"granted"===e?n.resolve(e):n.reject(e)}),n.promise},this.sendNotification=function(e,o){var t=new i(e,o);t.onerror=function(e){n.error(e)}},this.notify=function(e,t){"Notification"in window?"granted"!==i.permission?o.checkPermission().then(function(){return o.sendNotification(e,t)}):"granted"===i.permission&&o.sendNotification(e,t):n.info("This browser does not support desktop notification")}}])}(),function(){function e(){return{request:function(e){return e.url.indexOf(".html")===-1&&(e.url+="?no_hyperlinks=1"),e}}}angular.module("xos.helpers").factory("NoHyperlinks",e)}(),angular.module("xos.helpers").config(["$provide",function(e){e.decorator("$log",["$delegate",function(e){var n=function(){return window.location.href.indexOf("debug=true")>=0},i=e.log,o=e.info,t=e.warn,r=e.debug,a=function(i){return function(){if(n()){var o=[].slice.call(arguments),t=new Date;return o[0]="["+t.getHours()+":"+t.getMinutes()+":"+t.getSeconds()+"] "+o[0],!angular.isFunction(e.reset)||e.debug.logs instanceof Array||e.reset(),i.apply(null,o)}}};return e.info=a(o),e.log=a(i),e.warn=a(t),e.debug=a(r),e}])}]),function(){function e(e){return{request:function(n){return"GET"!==n.method&&(n.headers["X-CSRFToken"]=e.get("xoscsrftoken")),n}}}e.$inject=["$cookies"],angular.module("xos.helpers").factory("SetCSRFToken",e)}();
\ No newline at end of file
diff --git a/dist/xosUiComponents.js b/dist/xosUiComponents.js
index 0384480..2392930 100644
--- a/dist/xosUiComponents.js
+++ b/dist/xosUiComponents.js
@@ -28,8 +28,6 @@
 
   angular.module('xos.uiComponents', ['chart.js', 'RecursionHelper', 'dndLists']);
 })();
-//# sourceMappingURL=../maps/ui_components/ui-components.module.js.map
-
 'use strict';
 
 /**
@@ -249,8 +247,6 @@
     }]
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartTable/smartTable.component.js.map
-
 'use strict';
 
 /**
@@ -496,8 +492,6 @@
     }]
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartPie/smartPie.component.js.map
-
 'use strict';
 
 /**
@@ -592,8 +586,6 @@
     }
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/validation/validation.component.js.map
-
 'use strict';
 
 /**
@@ -1024,8 +1016,6 @@
     };
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/table/table.component.js.map
-
 'use strict';
 
 /**
@@ -1051,6 +1041,7 @@
     * ```
     * {
     *   exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
+    *   order: ['field1', 'field2'], // ordering the fields (missing ones are attached at the end)
     *   actions: [ // define the form buttons with related callback
     *     {
             label: 'save',
@@ -1085,6 +1076,7 @@
     *   }
     * }
     * ```
+    * @param {Object} ngModel The model object (it is mandatory to specify at least an empty object)
     * @element ANY
     * @scope
     * @requires xos.uiComponents.directive:xosField
@@ -1136,21 +1128,20 @@
         return $window._;
       })
       .controller('SampleCtrl1', function(SampleResource){
-          this.model = {
-        };
+         this.model = {};
          this.config = {
           exclude: ['password', 'last_login'],
           formName: 'sampleForm1',
           feedback: {
             show: false,
-            message: 'Form submitted successfully !!!',
+            message: 'Form submitted successfully!',
             type: 'success'
           },
           actions: [
             {
               label: 'Save',
-              icon: 'ok', // refers to bootstraps glyphicon
-              cb: (user) => { // receive the model
+              icon: 'ok',
+              cb: (user) => {
                 console.log(user);
                 this.config.feedback.show = true;
                 this.config.feedback.type='success';
@@ -1158,6 +1149,7 @@
               class: 'success'
             }
           ],
+          order: ['site', 'last_name', 'first_name', 'age'],
           fields: {
             first_name: {
               type: 'string',
@@ -1180,27 +1172,24 @@
                 min: 21
               }
             },
-             site: {
-            label: 'Site',
-            type: 'select',
-            validators: { required: true},
-            hint: 'The Site this Slice belongs to',
-            options: []
+            site: {
+              label: 'Site',
+              type: 'select',
+              validators: { required: true},
+              hint: 'The Site this Slice belongs to',
+              options: []
             },
          }
         };
         SampleResource.query().$promise
-          .then((users) => {
-          //this.users_site = users;
-        //console.log(users);
+        .then((users) => {
           this.optionVal = users;
           this.config.fields['site'].options = this.optionVal;
-        //= this.optionVal;
-       })
-      .catch((e) => {
-        throw new Error(e);
+        })
+        .catch((e) => {
+          throw new Error(e);
+        });
       });
-       });
     </file>
    <file name="backend.js">
      angular.module('sampleForm1')
@@ -1212,7 +1201,7 @@
       .service('SampleResource', function($resource){
         return $resource('/test/:id', {id: '@id'});
       });
-     </file>
+    </file>
     <file name="index.html">
       <div ng-controller="SampleCtrl1 as vm">
         <xos-form ng-model="vm.model" config="vm.config"></xos-form>
@@ -1264,7 +1253,7 @@
         }
         var diff = _.difference(Object.keys(_this.ngModel), _this.excludedField);
         var modelField = XosFormHelpers.parseModelField(diff);
-        _this.formField = XosFormHelpers.buildFormStructure(modelField, _this.config.fields, _this.ngModel);
+        _this.formField = XosFormHelpers.buildFormStructure(modelField, _this.config.fields, _this.ngModel, _this.config.order);
       }, true);
 
       $scope.$watch(function () {
@@ -1282,8 +1271,6 @@
     }]
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/form/form.component.js.map
-
 'use strict';
 
 /**
@@ -1384,8 +1371,6 @@
     };
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/pagination/pagination.component.js.map
-
 'use strict';
 
 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
@@ -1472,14 +1457,25 @@
             };
              this.field5 = {
               name: 'select',
-              label: 'Select field:',
-              type: 'select',
-              model: 1,
-              options: [
-                {id: 1, label: 'One'},
-                {id: 2, label: 'Two'},
-                {id: 3, label: 'Three'},
-              ]
+              field: {
+                label: 'Select field:',
+                type: 'select',
+                options: [
+                  {id: 1, label: 'One'},
+                  {id: 2, label: 'Two'},
+                  {id: 3, label: 'Three'},
+                ]
+              },
+              model: 1
+            };
+             this.arrayField = {
+              name: 'array',
+              field: {
+                label: 'Array field:',
+                type: 'array',
+                options: ['one', 'two', 'three', 'four']
+              },
+              model: ['one', 'two'],
             };
           });
         </file>
@@ -1490,6 +1486,7 @@
             <xos-field ng-model="vm.field3.model" name="vm.field3.name" field="vm.field3.field"></xos-field>
             <xos-field ng-model="vm.field4.model" name="vm.field4.name" field="vm.field4.field"></xos-field>
             <xos-field ng-model="vm.field5.model" name="vm.field5.name" field="vm.field5.field"></xos-field>
+            <xos-field ng-model="vm.arrayField.model" name="vm.arrayField.name" field="vm.arrayField.field"></xos-field>
           </div>
         </file>
       </example>
@@ -1619,8 +1616,6 @@
     };
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/field/field.component.js.map
-
 'use strict';
 
 /**
@@ -1756,8 +1751,6 @@
     }]
   });
 })();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/alert/alert.component.js.map
-
 'use strict';
 
 (function () {
@@ -1856,8 +1849,6 @@
     };
   }
 })();
-//# sourceMappingURL=../../../maps/services/helpers/ui/label_formatter.service.js.map
-
 'use strict';
 
 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
@@ -1956,7 +1947,6 @@
     **/
 
     this.buildFormStructure = function (modelField, customField, model, order) {
-
       var orderedForm = {};
 
       modelField = angular.extend(modelField, customField);
@@ -2024,8 +2014,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../../../maps/services/helpers/ui/form.helpers.js.map
-
 'use strict';
 
 (function () {
@@ -2122,8 +2110,6 @@
     };
   }
 })();
-//# sourceMappingURL=../../../maps/services/helpers/ui/comparator.service.js.map
-
 'use strict';
 
 (function () {
@@ -2165,8 +2151,6 @@
     $resourceProvider.defaults.stripTrailingSlashes = false;
   }
 })();
-//# sourceMappingURL=maps/xosHelpers.module.js.map
-
 'use strict';
 
 (function () {
@@ -2182,8 +2166,6 @@
     return $resource('/api/service/vsg/');
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/vSG.js.map
-
 'use strict';
 
 (function () {
@@ -2201,8 +2183,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/vOLT.js.map
-
 'use strict';
 
 (function () {
@@ -2226,8 +2206,6 @@
     return $resource('/api/utility/logout/');
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Utility.js.map
-
 'use strict';
 
 (function () {
@@ -2245,8 +2223,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Users.js.map
-
 'use strict';
 
 (function () {
@@ -2264,8 +2240,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Truckroll.js.map
-
 'use strict';
 
 (function () {
@@ -2283,8 +2257,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Tenant.js.map
-
 'use strict';
 
 (function () {
@@ -2434,8 +2406,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Subscribers.js.map
-
 'use strict';
 
 (function () {
@@ -2473,8 +2443,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Slices_plus.js.map
-
 'use strict';
 
 (function () {
@@ -2492,8 +2460,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Slices.js.map
-
 'use strict';
 
 (function () {
@@ -2511,8 +2477,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Sites.js.map
-
 'use strict';
 
 (function () {
@@ -2530,8 +2494,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Services.js.map
-
 'use strict';
 
 (function () {
@@ -2547,8 +2509,6 @@
     return $resource('/api/service/onos/');
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/ONOS-Services.js.map
-
 'use strict';
 
 (function () {
@@ -2564,8 +2524,6 @@
     return $resource('/api/tenant/onos/app/');
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/ONOS-Apps.js.map
-
 'use strict';
 
 (function () {
@@ -2583,8 +2541,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Nodes.js.map
-
 'use strict';
 
 (function () {
@@ -2602,8 +2558,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Networkstemplates.js.map
-
 'use strict';
 
 (function () {
@@ -2621,8 +2575,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Networks.js.map
-
 'use strict';
 
 (function () {
@@ -2648,8 +2600,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Me.js.map
-
 'use strict';
 
 (function () {
@@ -2667,8 +2617,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Instances.js.map
-
 'use strict';
 
 (function () {
@@ -2686,8 +2634,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Images.js.map
-
 'use strict';
 
 (function () {
@@ -2705,8 +2651,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Flavors.js.map
-
 'use strict';
 
 (function () {
@@ -2722,8 +2666,6 @@
     return $resource('/api/service/exampleservice/');
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Example.js.map
-
 'use strict';
 
 (function () {
@@ -2741,8 +2683,6 @@
     });
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Deployments.js.map
-
 'use strict';
 
 (function () {
@@ -2774,8 +2714,6 @@
     return r;
   }]);
 })();
-//# sourceMappingURL=../../maps/services/rest/Dashboards.js.map
-
 'use strict';
 
 (function () {
@@ -2936,8 +2874,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../../maps/services/helpers/user-prefs.service.js.map
-
 'use strict';
 
 (function () {
@@ -2979,8 +2915,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../maps/services/service_graph.service.js.map
-
 'use strict';
 
 /* eslint-disable  angular/ng_window_service*/
@@ -3040,8 +2974,6 @@
     };
   }]);
 })();
-//# sourceMappingURL=../maps/services/notification.service.js.map
-
 'use strict';
 
 (function () {
@@ -3066,8 +2998,6 @@
     };
   }
 })();
-//# sourceMappingURL=../maps/services/noHyperlinks.interceptor.js.map
-
 'use strict';
 
 // TODO write tests for log
@@ -3127,8 +3057,6 @@
     return $delegate;
   }]);
 }]);
-//# sourceMappingURL=../maps/services/log.decorator.js.map
-
 'use strict';
 
 (function () {
@@ -3154,8 +3082,6 @@
     };
   }
 })();
-//# sourceMappingURL=../maps/services/csrfToken.interceptor.js.map
-
 /**
 * @ngdoc overview
 * @name ngXosLib
@@ -3169,5 +3095,4 @@
 * ## Issues
 * Please report issues at https://jira.opencord.org
 **/
-"use strict";
-//# sourceMappingURL=maps/index.ngdoc.js.map
+"use strict";
\ No newline at end of file
diff --git a/src/services/helpers/ui/form.helpers.js b/src/services/helpers/ui/form.helpers.js
index 331cdb0..5dbecd7 100644
--- a/src/services/helpers/ui/form.helpers.js
+++ b/src/services/helpers/ui/form.helpers.js
@@ -94,7 +94,6 @@
     **/
 
     this.buildFormStructure = (modelField, customField, model, order) => {
-
       const orderedForm = {};
 
       modelField = angular.extend(modelField, customField);
diff --git a/src/ui_components/dumbComponents/field/field.component.js b/src/ui_components/dumbComponents/field/field.component.js
index 6fc07ac..6b413bb 100644
--- a/src/ui_components/dumbComponents/field/field.component.js
+++ b/src/ui_components/dumbComponents/field/field.component.js
@@ -85,14 +85,26 @@
 
             this.field5 = {
               name: 'select',
-              label: 'Select field:',
-              type: 'select',
-              model: 1,
-              options: [
-                {id: 1, label: 'One'},
-                {id: 2, label: 'Two'},
-                {id: 3, label: 'Three'},
-              ]
+              field: {
+                label: 'Select field:',
+                type: 'select',
+                options: [
+                  {id: 1, label: 'One'},
+                  {id: 2, label: 'Two'},
+                  {id: 3, label: 'Three'},
+                ]
+              },
+              model: 1
+            };
+
+            this.arrayField = {
+              name: 'array',
+              field: {
+                label: 'Array field:',
+                type: 'array',
+                options: ['one', 'two', 'three', 'four']
+              },
+              model: ['one', 'two'],
             };
           });
         </file>
@@ -103,6 +115,7 @@
             <xos-field ng-model="vm.field3.model" name="vm.field3.name" field="vm.field3.field"></xos-field>
             <xos-field ng-model="vm.field4.model" name="vm.field4.name" field="vm.field4.field"></xos-field>
             <xos-field ng-model="vm.field5.model" name="vm.field5.name" field="vm.field5.field"></xos-field>
+            <xos-field ng-model="vm.arrayField.model" name="vm.arrayField.name" field="vm.arrayField.field"></xos-field>
           </div>
         </file>
       </example>
diff --git a/src/ui_components/dumbComponents/form/form.component.js b/src/ui_components/dumbComponents/form/form.component.js
index ecdddc6..185abc3 100644
--- a/src/ui_components/dumbComponents/form/form.component.js
+++ b/src/ui_components/dumbComponents/form/form.component.js
@@ -22,6 +22,7 @@
     * ```
     * {
     *   exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
+    *   order: ['field1', 'field2'], // ordering the fields (missing ones are attached at the end)
     *   actions: [ // define the form buttons with related callback
     *     {
             label: 'save',
@@ -56,6 +57,7 @@
     *   }
     * }
     * ```
+    * @param {Object} ngModel The model object (it is mandatory to specify at least an empty object)
     * @element ANY
     * @scope
     * @requires xos.uiComponents.directive:xosField
@@ -112,23 +114,21 @@
       })
       .controller('SampleCtrl1', function(SampleResource){
 
-
-        this.model = {
-        };
+        this.model = {};
 
         this.config = {
           exclude: ['password', 'last_login'],
           formName: 'sampleForm1',
           feedback: {
             show: false,
-            message: 'Form submitted successfully !!!',
+            message: 'Form submitted successfully!',
             type: 'success'
           },
           actions: [
             {
               label: 'Save',
-              icon: 'ok', // refers to bootstraps glyphicon
-              cb: (user) => { // receive the model
+              icon: 'ok',
+              cb: (user) => {
                 console.log(user);
                 this.config.feedback.show = true;
                 this.config.feedback.type='success';
@@ -136,6 +136,7 @@
               class: 'success'
             }
           ],
+          order: ['site', 'last_name', 'first_name', 'age'],
           fields: {
             first_name: {
               type: 'string',
@@ -158,29 +159,23 @@
                 min: 21
               }
             },
-
             site: {
-            label: 'Site',
-            type: 'select',
-            validators: { required: true},
-            hint: 'The Site this Slice belongs to',
-            options: []
+              label: 'Site',
+              type: 'select',
+              validators: { required: true},
+              hint: 'The Site this Slice belongs to',
+              options: []
             },
          }
         };
         SampleResource.query().$promise
-          .then((users) => {
-          //this.users_site = users;
-        //console.log(users);
+        .then((users) => {
           this.optionVal = users;
           this.config.fields['site'].options = this.optionVal;
-        //= this.optionVal;
-
-      })
-      .catch((e) => {
-        throw new Error(e);
-      });
-
+        })
+        .catch((e) => {
+          throw new Error(e);
+        });
       });
     </file>
    <file name="backend.js">
@@ -193,7 +188,6 @@
       .service('SampleResource', function($resource){
         return $resource('/test/:id', {id: '@id'});
       });
-
     </file>
     <file name="index.html">
       <div ng-controller="SampleCtrl1 as vm">
@@ -264,7 +258,7 @@
         }
         let diff = _.difference(Object.keys(this.ngModel), this.excludedField);
         let modelField = XosFormHelpers.parseModelField(diff);
-        this.formField = XosFormHelpers.buildFormStructure(modelField, this.config.fields, this.ngModel);
+        this.formField = XosFormHelpers.buildFormStructure(modelField, this.config.fields, this.ngModel, this.config.order);
       }, true);
 
       $scope.$watch(() => this.ngModel, (model) => {