From 6a949d1b148a77c7fa61cf99db38a187a5a7c690 Mon Sep 17 00:00:00 2001 From: David Baldwynn Date: Sun, 5 Jul 2015 21:29:05 -0700 Subject: [PATCH] added autosaving --- app/controllers/forms.server.controller.js | 3 +- app/models/form.server.model.js | 24 +++--- app/models/form_field.server.model.js | 22 +++-- .../controllers/header.client.controller.js | 2 +- .../create-form.client.controller.js | 6 +- .../view-form.client.controller.js | 84 ++++++++++++++++++- public/modules/forms/css/form.css | 12 +-- .../directives/auto-save.client.directive.js | 80 ++++++++++++++++++ ...ve.js => change-focus.client.directive.js} | 0 .../configure-form.client.directive.js | 7 +- .../directives/edit-form.client.directive.js | 37 ++++---- .../directives/field.client.directive.js | 5 +- .../form-locator.client.directive.js | 8 ++ .../on-finish-render.client.directive.js | 20 +++++ .../views/directiveViews/field/checkbox.html | 6 +- .../views/directiveViews/field/date.html | 6 +- .../views/directiveViews/field/dropdown.html | 6 +- .../views/directiveViews/field/email.html | 6 +- .../views/directiveViews/field/hidden.html | 2 +- .../views/directiveViews/field/link.html | 6 +- .../views/directiveViews/field/password.html | 6 +- .../views/directiveViews/field/radio.html | 6 +- .../views/directiveViews/field/textarea.html | 6 +- .../views/directiveViews/field/textfield.html | 6 +- .../directiveViews/form/configure-form.html | 31 +++++-- .../views/directiveViews/form/edit-form.html | 36 ++++---- .../forms/views/view-form.client.view.html | 4 +- public/modules/users/services/auth.js | 4 +- 28 files changed, 334 insertions(+), 107 deletions(-) create mode 100644 public/modules/forms/directives/auto-save.client.directive.js rename public/modules/forms/directives/{changeFocus.client.directive.js => change-focus.client.directive.js} (100%) create mode 100644 public/modules/forms/directives/form-locator.client.directive.js create mode 100644 public/modules/forms/directives/on-finish-render.client.directive.js diff --git a/app/controllers/forms.server.controller.js b/app/controllers/forms.server.controller.js index 3cd580f7..70e53efc 100644 --- a/app/controllers/forms.server.controller.js +++ b/app/controllers/forms.server.controller.js @@ -201,7 +201,8 @@ exports.update = function(req, res) { }); } else { console.log('updated form'); - res.json(form); + // res.json(form); + res.status(200).send('updated form'); } }); }; diff --git a/app/models/form.server.model.js b/app/models/form.server.model.js index 68f813bf..c0066770 100644 --- a/app/models/form.server.model.js +++ b/app/models/form.server.model.js @@ -120,17 +120,19 @@ FormSchema.pre('save', function (next) { }); //Concatenate submission and form's form_fields -FormSchema.pre('save', function (next) { - if(this.isModified('form_fields')){ - if(this.submissions.length){ - for(var i=0; i -1) { @@ -52,6 +59,9 @@ var FormFieldSchema = new Schema({ type: String, default: '', }, + options: [{ + type: String + }] required: { type: Boolean, default: true, diff --git a/public/modules/core/controllers/header.client.controller.js b/public/modules/core/controllers/header.client.controller.js index 0d584c2e..c5e7e377 100755 --- a/public/modules/core/controllers/header.client.controller.js +++ b/public/modules/core/controllers/header.client.controller.js @@ -5,7 +5,7 @@ angular.module('core').controller('HeaderController', ['$rootScope','$scope','Me $scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User); $scope.authentication = $rootScope.authentication = Auth; - console.log('isAuthenticated(): '+$scope.authentication.isAuthenticated()); + // console.log('isAuthenticated(): '+$scope.authentication.isAuthenticated()); $scope.isCollapsed = false; $scope.hideNav = false; diff --git a/public/modules/forms/controllers/create-form.client.controller.js b/public/modules/forms/controllers/create-form.client.controller.js index e0e19325..d2946dc1 100644 --- a/public/modules/forms/controllers/create-form.client.controller.js +++ b/public/modules/forms/controllers/create-form.client.controller.js @@ -71,12 +71,12 @@ angular.module('forms').controller('EditFormController', ['$scope', '$state', '$ } }; - $scope.goToWithId = function(route, id) { + $rootScope.goToWithId = function(route, id) { $state.go(route, {'formId': id}, {reload: true}); }; // Create new Form - $scope.createOrUpdate = function() { + $rootScope.createOrUpdate = function() { if($scope.isNewForm){ // Create new Form object @@ -100,7 +100,7 @@ angular.module('forms').controller('EditFormController', ['$scope', '$state', '$ }; // Update existing Form - $scope.update = function() { + $rootScope.update = function() { var form = new Forms($scope.form); console.log('update form'); console.log($scope.form); diff --git a/public/modules/forms/controllers/view-form.client.controller.js b/public/modules/forms/controllers/view-form.client.controller.js index 3d15c45e..f18e4de3 100644 --- a/public/modules/forms/controllers/view-form.client.controller.js +++ b/public/modules/forms/controllers/view-form.client.controller.js @@ -1,8 +1,8 @@ 'use strict'; // Forms controller -angular.module('forms').controller('ViewFormController', ['$scope', '$stateParams', '$state', 'Forms', 'CurrentForm','$http', - function($scope, $stateParams, $state, Forms, CurrentForm, $http) { +angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope', '$stateParams', '$state', 'Forms', 'CurrentForm','$http', + function($rootScope, $scope, $stateParams, $state, Forms, CurrentForm, $http) { // view form submissions $scope.form = CurrentForm.getForm(); @@ -13,6 +13,26 @@ angular.module('forms').controller('ViewFormController', ['$scope', '$stateParam rows: [] }; + $scope.saveInProgress = false; + $scope.update = function() { + if(!$scope.saveInProgress){ + $scope.saveInProgress = true; + + console.log('start update()'); + + $http.put('/forms/'+$scope.form._id, {form: $scope.form}) + .then(function(response){ + console.log('form updated successfully'); + console.log('$scope.saveInProgress: '+$scope.saveInProgress); + // $rootScope.goToWithId('viewForm', $scope.form._id); + }).catch(function(response){ + console.log('Error occured during form UPDATE.\n'); + console.log(response.data); + }).finally(function() { + $scope.saveInProgress = false; + }); + }; + } //Table Functions $scope.toggleAllCheckers = function(){ @@ -94,7 +114,65 @@ angular.module('forms').controller('ViewFormController', ['$scope', '$stateParam console.log('ERROR: Form could not be deleted.'); console.error(error); }); - }; + + $scope.goToWithId = function(route, id) { + $state.go(route, {'formId': id}, {reload: true}); + }; + + // Create new Form + $rootScope.createOrUpdate = function() { + if($scope.isNewForm){ + // Create new Form object + var form = new Forms($scope.form); + + $http.post('/forms', {form: $scope.form}) + .success(function(data, status, headers){ + console.log('form created'); + + // Clear form fields + $scope.form = {}; + // Redirect after save + $scope.goToWithId('viewForm', $scope.form._id); + }).error(function(errorResponse){ + console.log(errorResponse.data.message); + $scope.error = errorResponse.data.message; + }); + } else{ + $rootScope.update(); + } + }; + + // $rootScope.saveInProgress = false; + + var saveFinished = function() { + $rootScope.saveInProgress = false; + console.log('update form'); + }; + + // Update existing Form + $rootScope.update = function() { + + $rootScope.saveInProgress = true; + console.log('update form'); + + $http.put('/forms/'+$scope.form._id, {form: $scope.form}) + .then(function(response){ + console.log('form updated successfully'); + }).catch(function(response){ + console.log('Error occured during form UPDATE.\n'); + console.log(response.data); + }).finally(function() { + $rootScope.saveInProgress = false; + console.log('update form'); + }); + }; + + $rootScope.resetForm = function(){ + $scope.form = Forms.get({ + formId: $stateParams.formId + }); + } + } ]); \ No newline at end of file diff --git a/public/modules/forms/css/form.css b/public/modules/forms/css/form.css index 7ca8d85c..6e85f312 100644 --- a/public/modules/forms/css/form.css +++ b/public/modules/forms/css/form.css @@ -24,7 +24,7 @@ form .row.field { width:600px; } -form.config-form > .row { +div.config-form > .row { padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; @@ -35,16 +35,16 @@ form.config-form > .row { width: 90%; } - form.config-form > .row > .container:nth-of-type(odd){ + div.config-form > .row > .container:nth-of-type(odd){ border-right: 1px #ddd solid; /*padding-left: 1em;*/ } - form.config-form .row > .field-input { + div.config-form .row > .field-input { padding-top:1.2em; padding-left:0.1em; } - form.config-form .row > .field-input label { + div.config-form .row > .field-input label { padding-left:1.3em; display: block; } @@ -126,10 +126,10 @@ form.config-form > .row { padding-left:0.6em; } .status-light.status-light-off { - color: red; + color: #BE0000; } .status-light.status-light-on { - color: green; + color: #33CC00; } /* Styles for form list view (/forms) */ diff --git a/public/modules/forms/directives/auto-save.client.directive.js b/public/modules/forms/directives/auto-save.client.directive.js new file mode 100644 index 00000000..dc41ee0f --- /dev/null +++ b/public/modules/forms/directives/auto-save.client.directive.js @@ -0,0 +1,80 @@ +'use strict'; + +angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', function($rootScope, $timeout) { + + return { + require: ['^form'], + link: function($scope, $element, $attrs, $ctrls) { + + var difference = function(array){ + var rest = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1)); + + var containsEquals = function(obj, target) { + if (obj == null) return false; + return _.any(obj, function(value) { + return _.isEqual(value, target); + }); + }; + + return _.filter(array, function(value){ return !containsEquals(rest, value); }); + }; + + var $formCtrl = $ctrls[0]; + var savePromise = null; + $scope.finishedRender = false; + var expression = $attrs.autoSaveForm || 'true'; + + $scope.$on('ngRepeatStarted', function(ngRepeatFinishedEvent) { + // $scope.finishedRender = false; + }); + $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { + $scope.finishedRender = true; + }); + + $scope.$watch('form', function(newValue, oldValue) { + // console.log('auto saving'); + // console.log(oldValue); + // console.log(newValue); + if(difference(oldValue.form_fields,newValue.form_fields).length !== 0 && !$formCtrl.$dirty) { + $formCtrl.$setDirty(); + } + // else if(difference(oldValue.form_fields,newValue.form_fields).length === 0 ){ + // $scope.finishedRender = true; + // } + // console.log('\n\n-------\n$pristine: '+( $formCtrl.$pristine ) ); + // console.log('$dirty: '+( $formCtrl.$dirty ) ); + console.log('form_fields changed: '+difference(oldValue.form_fields,newValue.form_fields).length ); + console.log('$valid: '+$formCtrl.$valid); + console.log('finishedRender: '+$scope.finishedRender); + console.log('saveInProgress: '+$scope.saveInProgress); + + if($scope.finishedRender && ($formCtrl.$dirty || difference(oldValue.form_fields,newValue.form_fields).length !== 0)) { + console.log('auto saving'); + + if(savePromise) { + $timeout.cancel(savePromise); + } + + savePromise = $timeout(function() { + savePromise = null; + + // Still valid? + if(true) { + // console.log('inside'); + + if($scope.$eval(expression) !== false) { + console.log('Form data persisted -- setting pristine flag'); + $formCtrl.$setPristine(); + // $scope.finishedRender = false; + } + + } + + }); + } + + }, true); + } + }; + +}]); \ No newline at end of file diff --git a/public/modules/forms/directives/changeFocus.client.directive.js b/public/modules/forms/directives/change-focus.client.directive.js similarity index 100% rename from public/modules/forms/directives/changeFocus.client.directive.js rename to public/modules/forms/directives/change-focus.client.directive.js diff --git a/public/modules/forms/directives/configure-form.client.directive.js b/public/modules/forms/directives/configure-form.client.directive.js index 0c51c080..a834f01f 100644 --- a/public/modules/forms/directives/configure-form.client.directive.js +++ b/public/modules/forms/directives/configure-form.client.directive.js @@ -1,13 +1,14 @@ 'use strict'; -angular.module('forms').directive('configureFormDirective', ['$http', '$timeout', 'timeCounter', 'Auth', 'FormFields', - function ($http, $timeout, timeCounter, Auth, FormFields) { +angular.module('forms').directive('configureFormDirective', ['$rootScope','$http', '$timeout', 'timeCounter', 'Auth', 'FormFields', + function ($rootScope, $http, $timeout, timeCounter, Auth, FormFields) { return { controller: function($scope){ $scope.log = ''; $scope.pdfLoading = false; var _current_upload = null; - + $scope.createOrUpdate = $rootScope.createOrUpdate; + $scope.resetForm = $rootScope.resetForm; var _unbindedPdfFields = $scope.pdfFields; diff --git a/public/modules/forms/directives/edit-form.client.directive.js b/public/modules/forms/directives/edit-form.client.directive.js index 421d32f1..23958a41 100644 --- a/public/modules/forms/directives/edit-form.client.directive.js +++ b/public/modules/forms/directives/edit-form.client.directive.js @@ -1,15 +1,27 @@ 'use strict'; -angular.module('forms').directive('editFormDirective', ['$http', '$timeout', 'timeCounter', 'Auth', 'FormFields', - function ($http, $timeout, timeCounter, Auth, FormFields) { +angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$http', '$timeout', 'timeCounter', 'Auth', 'FormFields', + function ($rootScope, $q, $http, $timeout, timeCounter, Auth, FormFields) { return { + // link: function (scope, iElm, iAttrs) { + // console.log(scope); + // }, + templateUrl: './modules/forms/views/directiveViews/form/edit-form.html', + restrict: 'E', + scope: { + form:'=', + user:'=' + }, controller: function($scope){ + //Populate local scope with rootScope methods/variables + $scope.update = $rootScope.update; + //Populate AddField with all available form field types $scope.addField = {}; $scope.addField.types = FormFields.fields; - // $scope.addField.new = $scope.addField.types[0].name; + $scope.addField.types.forEach(function(type){ - type.lastAddedID = 0; + type.lastAddedID = 1; return type; }); @@ -24,9 +36,10 @@ angular.module('forms').directive('editFormDirective', ['$http', '$timeout', 'ti $scope.addField.lastAddedID++; var fieldTitle; for(var i = 0; i < $scope.addField.types.length; i++){ - console.log($scope.addField.types[i].name === fieldType); + // console.log($scope.addField.types[i].name === fieldType); if($scope.addField.types[i].name === fieldType){ $scope.addField.types[i].lastAddedID++; + console.log($scope.addField.types[i].lastAddedID); fieldTitle = $scope.addField.types[i].value+$scope.addField.types[i].lastAddedID; break; } @@ -64,11 +77,6 @@ angular.module('forms').directive('editFormDirective', ['$http', '$timeout', 'ti } }; - $scope.hover = function(field) { - // Shows/hides the delete button on hover - return field.showTools = !field.showTools; - }; - // add new option to the field $scope.addOption = function (field){ if(!field.field_options) @@ -104,19 +112,14 @@ angular.module('forms').directive('editFormDirective', ['$http', '$timeout', 'ti // decides whether field options block will be shown (true for dropdown and radio fields) $scope.showAddOptions = function (field){ - if(field.field_type === 'radio' || field.field_type === 'dropdown') + if(field.fieldType == 'dropdown' || field.fieldType == 'checkbox' || field.fieldType == 'scale' || field.fieldType == 'rating' || field.fieldType == 'radio') return true; else return false; }; }, - templateUrl: './modules/forms/views/directiveViews/form/edit-form.html', - restrict: 'E', - scope: { - form:'=', - user:'=' - } + }; } ]); \ No newline at end of file diff --git a/public/modules/forms/directives/field.client.directive.js b/public/modules/forms/directives/field.client.directive.js index 9af21cee..7592d98c 100644 --- a/public/modules/forms/directives/field.client.directive.js +++ b/public/modules/forms/directives/field.client.directive.js @@ -32,6 +32,8 @@ angular.module('forms').directive('fieldDirective', function($http, $compile) { }; var linker = function(scope, element) { + scope.field.required = scope.required; + // GET template content from path var templateUrl = getTemplateUrl(scope.field); $http.get(templateUrl).success(function(data) { @@ -44,7 +46,8 @@ angular.module('forms').directive('fieldDirective', function($http, $compile) { template: '
{{field.title}}
', restrict: 'E', scope: { - field: '=' + field: '=', + required: '&' }, link: linker }; diff --git a/public/modules/forms/directives/form-locator.client.directive.js b/public/modules/forms/directives/form-locator.client.directive.js new file mode 100644 index 00000000..9d9f8ee4 --- /dev/null +++ b/public/modules/forms/directives/form-locator.client.directive.js @@ -0,0 +1,8 @@ +'use strict'; +angular.module('forms').directive('formLocator', function() { + return { + link: function(scope) { + scope.$emit('formLocator'); + } + } +}); \ No newline at end of file diff --git a/public/modules/forms/directives/on-finish-render.client.directive.js b/public/modules/forms/directives/on-finish-render.client.directive.js new file mode 100644 index 00000000..1612b2ae --- /dev/null +++ b/public/modules/forms/directives/on-finish-render.client.directive.js @@ -0,0 +1,20 @@ +'use strict'; + +angular.module('forms').directive('onFinishRender', function ($rootScope, $timeout) { + return { + restrict: 'A', + link: function (scope, element, attr) { + if (scope.$first === true) { + $timeout(function () { + $rootScope.$broadcast('ngRepeatStarted'); + }, 500); + } + if (scope.$last === true) { + $timeout(function () { + // console.log('ngRepeatFinished') + $rootScope.$broadcast('ngRepeatFinished'); + }, 500); + } + } + } +}); diff --git a/public/modules/forms/views/directiveViews/field/checkbox.html b/public/modules/forms/views/directiveViews/field/checkbox.html index 947a48ce..012127b2 100755 --- a/public/modules/forms/views/directiveViews/field/checkbox.html +++ b/public/modules/forms/views/directiveViews/field/checkbox.html @@ -1,7 +1,7 @@
-
{{field.title}}
-
- +
{{field.title}}
+
+ (* required)
diff --git a/public/modules/forms/views/directiveViews/field/date.html b/public/modules/forms/views/directiveViews/field/date.html index ad6ef648..ced056c6 100755 --- a/public/modules/forms/views/directiveViews/field/date.html +++ b/public/modules/forms/views/directiveViews/field/date.html @@ -1,9 +1,9 @@
-
{{field.title}}
+
{{field.title}}
* required -
+
- +
diff --git a/public/modules/forms/views/directiveViews/field/dropdown.html b/public/modules/forms/views/directiveViews/field/dropdown.html index b3b0533a..fcac1641 100755 --- a/public/modules/forms/views/directiveViews/field/dropdown.html +++ b/public/modules/forms/views/directiveViews/field/dropdown.html @@ -1,7 +1,7 @@
-
{{field.title}}
-
-