1
0

fixed autosaving

This commit is contained in:
David Baldwynn 2015-08-05 15:24:24 -07:00
parent 21a4eed173
commit 3d63357831
13 changed files with 673 additions and 417 deletions

29
docs/ux_flows.md Normal file
View File

@ -0,0 +1,29 @@
UX of Updating Forms
====================
##Form Updating
- Action: Update Configuration
1. User clicks on Form from FormList page (visits form admin page)
2. User clicks on "Configure Form" Tab
3. User changes inputs in form page
4. Save Button is Pressed
5. Loading/Busy Indicator fills the screen
6. Loading/Busy Indicator exits the screen
7. Configuration page is shown with updated settings
- Action: Add Form Field
1. User clicks on Form from FormList page
2. User clicks on a Tab in "AddField" column
3. Loading/Busy Indicator fills the screen
4. Loading/Busy Indicator exits the screen
- Action: Edit Form Field Title
1. User clicks on Form from FormList page (visits form admin page)
2. User clicks on a current Form Input accordion (must be either the name or the caret)
3. The clicked accordion (the one interacted with in Step#2) expands
4. User clicks on 'Question Title' text input (aka focuses on said text input)
5. User starts typing in text input
6. User defocuses/clicks off of text input
7. Loading/Busy Indicator fills the screen
8. Loading/Busy Indicator exits the screen
9. Field Accordion heading is updated

View File

@ -451,11 +451,9 @@ angular.module('forms').controller('ListFormsController', ['$rootScope', '$scope
// Return all user's Forms // Return all user's Forms
$scope.findAll = function() { $scope.findAll = function() {
if(!$scope.myforms){
Forms.query(function(_forms){ Forms.query(function(_forms){
$scope.myforms = _forms; $scope.myforms = _forms;
}); });
}
}; };
//Modal functions //Modal functions
@ -473,6 +471,9 @@ angular.module('forms').controller('ListFormsController', ['$rootScope', '$scope
$scope.setForm = function (form) { $scope.setForm = function (form) {
$scope.myform = form; $scope.myform = form;
}; };
$scope.goToWithId = function(route, id) {
$state.go(route, {'formId': id}, {reload: true});
};
// Create new Form // Create new Form
$scope.createNew = function(){ $scope.createNew = function(){
@ -498,6 +499,37 @@ angular.module('forms').controller('ListFormsController', ['$rootScope', '$scope
}); });
} }
}; };
$scope.remove = function(form_id) {
console.log('Remove existing form');
var form = {};
if(!form_id){
form = CurrentForm.getForm();
if(!form) form = $scope.myform;
}else {
form._id = form_id;
}
$http.delete('/forms/'+form._id)
.success(function(data, status, headers){
console.log('form deleted successfully');
if(!form_id){
$state.go('listForms', {}, {reload: true});
}
if($scope.myforms.length > 0){
$scope.myforms = _.filter($scope.myforms, function(myform){
return myform._id !== form._id;
});
}
}).error(function(error){
console.log('ERROR: Form could not be deleted.');
console.error(error);
});
};
} }
]); ]);
'use strict'; 'use strict';
@ -535,10 +567,12 @@ angular.module('forms').controller('SubmitFormController', ['$scope', '$rootScop
'use strict'; 'use strict';
// Forms controller // Forms controller
angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope', '$stateParams', '$state', 'Forms', 'CurrentForm','$http', angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope', '$stateParams', '$state', 'Forms', 'CurrentForm', '$http', '$modal',
function($rootScope, $scope, $stateParams, $state, Forms, CurrentForm, $http) { function($rootScope, $scope, $stateParams, $state, Forms, CurrentForm, $http, $modal) {
var deleteModal;
$scope = $rootScope; $scope = $rootScope;
$scope.myform = CurrentForm.getForm(); $scope.myform = CurrentForm.getForm();
$rootScope.saveInProgress = false; $rootScope.saveInProgress = false;
$scope.viewSubmissions = false; $scope.viewSubmissions = false;
@ -555,13 +589,14 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
CurrentForm.setForm($scope.myform); CurrentForm.setForm($scope.myform);
}; };
$scope.goToWithId = function(route, id) {
$state.go(route, {'formId': id}, {reload: true});
};
$scope.setForm = function (form) { $scope.setForm = function (form) {
$scope.myform = form; $scope.myform = form;
}; };
$rootScope.resetForm = function(){
$scope.myform = Forms.get({
formId: $stateParams.formId
});
};
/* /*
* Table Functions * Table Functions
@ -673,8 +708,29 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
$scope.viewSubmissions = false; $scope.viewSubmissions = false;
}; };
/*
** DeleteModal Functions
*/
$scope.openDeleteModal = function() {
deleteModal = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ViewFormController',
});
};
$scope.cancelDeleteModal = function(){
if(deleteModal){
deleteModal.dismiss('cancel');
}
};
// Remove existing Form // Remove existing Form
$scope.remove = function(form_id) { $scope.remove = function(form_id) {
if(deleteModal && deleteModal.opened){
deleteModal.close();
var form = {}; var form = {};
if(!form_id){ if(!form_id){
form = CurrentForm.getForm(); form = CurrentForm.getForm();
@ -688,7 +744,7 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
console.log('form deleted successfully'); console.log('form deleted successfully');
if(!form_id){ if(!form_id){
$state.go('listForms'); $state.go('listForms', {}, {reload: true});
} }
if($scope.myforms.length > 0){ if($scope.myforms.length > 0){
$scope.myforms = _.filter($scope.myforms, function(myform){ $scope.myforms = _.filter($scope.myforms, function(myform){
@ -699,7 +755,10 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
}).error(function(error){ }).error(function(error){
console.log('ERROR: Form could not be deleted.'); console.log('ERROR: Form could not be deleted.');
console.error(error); console.error(error);
}).finally(function(){
}); });
}
}; };
@ -730,12 +789,6 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
} }
}; };
$rootScope.resetForm = function(){
$scope.myform = Forms.get({
formId: $stateParams.formId
});
};
} }
]); ]);
'use strict'; 'use strict';
@ -743,61 +796,77 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', function($rootScope, $timeout) { angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', function($rootScope, $timeout) {
return { return {
require: ['^form'], // require: ['^form'],
// scope: { restrict: 'AE',
// callback: '&autoSaveCallback'
// },
link: function($scope, $element, $attrs, $ctrls) { link: function($scope, $element, $attrs, $ctrls) {
angular.element(document).ready(function() {
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
if($rootScope.watchCount === undefined){
$rootScope.watchCount = 0;
}
var difference = function(array){ // Log that the directive has been linked.
var rest = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1)); // console.log( "Linked: autoSaveForm");
var containsEquals = function(obj, target) { var $formCtrl = $scope.editForm,
if (obj === null) return false; savePromise = null;
return _.any(obj, function(value) {
return _.isEqual(value, target);
});
};
return _.filter(array, function(value){ return !containsEquals(rest, value); });
};
var $formCtrl = $ctrls[0]; $scope.$on('editFormFieldsStarted', function(ngRepeatFinishedEvent) {
var savePromise = null;
// $scope.finishedRender = false;
var expression = $attrs.autoSaveForm || 'true';
$scope.$on('ngRepeatStarted', function(ngRepeatFinishedEvent) {
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
$rootScope.watchCount = 0;
}); });
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { $scope.$on('editFormFieldsFinished', function(ngRepeatFinishedEvent) {
$rootScope.finishedRender = true; $rootScope.finishedRender = true;
}); });
$scope.$watch('myform.form_fields', function(newValue, oldValue) { $scope.anyDirtyAndTouched = function (form){
console.log('watchCount: '+$rootScope.watchCount); for(var prop in form) {
if(difference(oldValue,newValue).length === 0 || oldValue === undefined){ if(form.hasOwnProperty(prop) && prop[0] !== '$') {
// console.log(form[prop]);
if(form[prop].$dirty && form[prop].$untouched) {
return true;
}
}
}
return false;
};
// console.log('properties of $scope.editForm');
// for(var item in $scope.editForm){
// console.log(item);
// }
// console.log($scope.editForm);
// console.log($scope.watchModel);
//Autosave Form when model (specificed in $attrs.autoSaveWatch) changes
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
if( !newValue && !oldValue ){
return; return;
} }
var changedFields = !_.isEqual(oldValue,newValue);
// console.log('\n\n----------\n$dirty: '+( $formCtrl.$dirty ) ); // console.log('\n\n----------');
// console.log('form_fields changed: '+difference(oldValue,newValue).length ); // console.log('$dirty: '+ $formCtrl.$dirty );
// console.log('$valid: '+$formCtrl.$valid); // console.log('oldValue: '+oldValue);
// console.log('newValue: '+newValue);
// console.log('form_fields changed: '+changedFields);
// console.log('finishedRender: '+$rootScope.finishedRender); // console.log('finishedRender: '+$rootScope.finishedRender);
// console.log('saveInProgress: '+$rootScope.saveInProgress); // console.log('saveInProgress: '+$rootScope.saveInProgress);
// console.log($scope.editForm);
var inputDirtyUntouched = $scope.anyDirtyAndTouched($scope.editForm);
if($rootScope.finishedRender && ($formCtrl.$dirty || difference(oldValue,newValue).length !== 0) && !$rootScope.saveInProgress) { // console.log('properties of $scope.editForm');
$rootScope.watchCount++; // for(var item in $scope.editForm){
// console.log(item);
// }
// if($rootScope.watchCount === 1) { // console.log('inputDirtyUntouched: '+inputDirtyUntouched);
// console.log($scope.editForm);
//Save form ONLY IF rendering is finished, form_fields have been change AND currently not save in progress
if($rootScope.finishedRender && ($formCtrl.$dirty || changedFields) && !$rootScope.saveInProgress) {
console.log('Saving Form');
if(savePromise) { if(savePromise) {
$timeout.cancel(savePromise); $timeout.cancel(savePromise);
} }
@ -811,12 +880,11 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
console.log('\n\nForm data persisted -- setting pristine flag'); console.log('\n\nForm data persisted -- setting pristine flag');
// console.log('\n\n---------\nUpdate form CLIENT'); // console.log('\n\n---------\nUpdate form CLIENT');
// console.log(Date.now()); // console.log(Date.now());
$rootScope.watchCount = 0;
$formCtrl.$setPristine(); $formCtrl.$setPristine();
// $rootScope.saveInProgress = false; // $formCtrl.$setUntouched();
}else{ }else{
console.log('Error form data NOT persisted'); console.error('Error form data NOT persisted');
console.log(err); console.error(err);
} }
}); });
@ -826,12 +894,29 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
}else{ }else{
return; return;
} }
}, true); }, true);
});
} }
}; };
}]); }]);
'use strict';
angular.module('forms').directive('bnLifecycle',['$rootScope', function($rootScope) {
return {
// I link the DOM element to the view model.
restrict: 'A',
link: function( scope, element, attributes ) {
// Log that the directive has been linked.
console.log('aoeuaoeu');
// console.log( 'Linked:', attributes.id );
}
};
}]);
'use strict'; 'use strict';
angular.module('forms').directive('changeFocus', function() { angular.module('forms').directive('changeFocus', function() {
@ -873,6 +958,14 @@ angular.module('forms').directive('changeFocus', function() {
angular.module('forms').directive('configureFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'timeCounter', 'Auth', 'FormFields', angular.module('forms').directive('configureFormDirective', ['$rootScope', '$http', 'Upload', '$timeout', 'timeCounter', 'Auth', 'FormFields',
function ($rootScope, $http, Upload, $timeout, timeCounter, Auth, FormFields) { function ($rootScope, $http, Upload, $timeout, timeCounter, Auth, FormFields) {
return { return {
templateUrl: './modules/forms/views/directiveViews/form/configure-form.html',
restrict: 'E',
scope: {
myform:'=',
user:'=',
pdfFields:'@',
formFields:'@'
},
controller: function($scope){ controller: function($scope){
$scope.log = ''; $scope.log = '';
$scope.pdfLoading = false; $scope.pdfLoading = false;
@ -943,30 +1036,43 @@ angular.module('forms').directive('configureFormDirective', ['$rootScope', '$htt
} }
}; };
},
templateUrl: './modules/forms/views/directiveViews/form/configure-form.html',
restrict: 'E',
scope: {
myform:'=',
user:'=',
pdfFields:'@',
formFields:'@'
} }
}; };
} }
]); ]);
'use strict'; 'use strict';
angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$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) { function ($rootScope, $q, $http, $timeout, timeCounter, Auth, FormFields) {
return { return {
templateUrl: './modules/forms/views/directiveViews/form/edit-form.html', templateUrl: './modules/forms/views/directiveViews/form/edit-form.html',
restrict: 'E', restrict: 'E',
scope: { scope: {
myform:'=', myform:'=',
user:'='
}, },
// transclude: true,
controller: function($scope){ controller: function($scope){
// Log that the directive has been linked.
// console.log( "Linked: editForm Controller");
/*
** Initialize scope with variables
*/
//Populate AddField with all available form field types
$scope.addField = {};
$scope.addField.types = FormFields.fields;
$scope.addField.types.forEach(function(type){
type.lastAddedID = 1;
return type;
});
// accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
//Populate local scope with rootScope methods/variables //Populate local scope with rootScope methods/variables
$scope.update = $rootScope.update; $scope.update = $rootScope.update;
@ -977,7 +1083,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
handle: ' .handle' handle: ' .handle'
} }
console.log($scope.myform); // console.log($scope.myform);
// $scope.draggable = { // $scope.draggable = {
// connectWith: ".dropzone", // connectWith: ".dropzone",
@ -1024,19 +1130,9 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
// }; // };
//Populate AddField with all available form field types /*
$scope.addField = {}; ** Field CRUD Methods
$scope.addField.types = FormFields.fields; */
$scope.addField.types.forEach(function(type){
type.lastAddedID = 1;
return type;
});
// accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
// Add a new field // Add a new field
$scope.addNewField = function(fieldType){ $scope.addNewField = function(fieldType){
@ -1088,6 +1184,10 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
} }
}; };
/*
** Field Option Methods
*/
// add new option to the field // add new option to the field
$scope.addOption = function (field){ $scope.addOption = function (field){
if(!field.fieldOptions) field.fieldOptions = []; if(!field.fieldOptions) field.fieldOptions = [];
@ -1139,7 +1239,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
angular.module('forms').directive('fieldIconDirective', function($http, $compile) { angular.module('forms').directive('fieldIconDirective', function($http, $compile) {
return { return {
templateUrl: './modules/forms/views/directiveViews/form/fieldIcon.html', template: '<i class="{{typeIcon}}"></i>',
restrict: 'E', restrict: 'E',
scope: { scope: {
typeName: '@' typeName: '@'
@ -1160,6 +1260,7 @@ angular.module('forms').directive('fieldIconDirective', function($http, $compile
'scale': 'fa fa-sliders', 'scale': 'fa fa-sliders',
'stripe': 'fa fa-credit-card', 'stripe': 'fa fa-credit-card',
'statement': 'fa fa-quote-left', 'statement': 'fa fa-quote-left',
'yes_no': 'fa fa-toggle-on'
} }
$scope.typeIcon = iconTypeMap[$scope.typeName]; $scope.typeIcon = iconTypeMap[$scope.typeName];
}, },
@ -1196,7 +1297,8 @@ angular.module('forms').directive('fieldDirective', function($http, $compile) {
'legal', 'legal',
'statement', 'statement',
'rating', 'rating',
'yes_no' 'yes_no',
'natural'
]; ];
if (__indexOf.call(supported_fields, type) >= 0) { if (__indexOf.call(supported_fields, type) >= 0) {
return templateUrl += type + '.html'; return templateUrl += type + '.html';
@ -1204,7 +1306,6 @@ angular.module('forms').directive('fieldDirective', function($http, $compile) {
}; };
var linker = function(scope, element) { var linker = function(scope, element) {
// scope.field.required = scope.required;
//Set format only if field is a date //Set format only if field is a date
if(scope.field.fieldType === 'date'){ if(scope.field.fieldType === 'date'){
@ -1216,6 +1317,15 @@ angular.module('forms').directive('fieldDirective', function($http, $compile) {
defaultDate: 0, defaultDate: 0,
}; };
} }
//Set only if we have a natural lang processing field
else if(scope.field.fieldType === 'natural'){
scope.field.fieldMatchValue = '';
//Fires when field is changed
scope.$watch('scope.field', function(newField, oldField) {
});
}
// GET template content from path // GET template content from path
var templateUrl = getTemplateUrl(scope.field); var templateUrl = getTemplateUrl(scope.field);
@ -1248,17 +1358,27 @@ angular.module('forms').directive('formLocator', function() {
angular.module('forms').directive('onFinishRender', function ($rootScope, $timeout) { angular.module('forms').directive('onFinishRender', function ($rootScope, $timeout) {
return { return {
restrict: 'A', restrict: 'A',
link: function (scope, element, attr) { link: function (scope, element, attrs) {
if (scope.$first === true) {
$timeout(function () { //Don't do anything if we don't have a ng-repeat on the current element
$rootScope.$broadcast('ngRepeatStarted'); if(!element.attr('ng-repeat')){
}); return;
} }
if (scope.$last === true) {
console.log(element); var broadcastMessage = attrs.onFinishRender || 'ngRepeat';
if(scope.$first && !scope.$last) {
$timeout(function () { $timeout(function () {
console.log('ngRepeatFinished') // console.log(broadcastMessage+'Started');
$rootScope.$broadcast('ngRepeatFinished'); $rootScope.$broadcast(broadcastMessage+'Started');
});
}else if(scope.$last) {
$timeout(function () {
element.ready(function () {
// console.log(broadcastMessage+'Finished');
$rootScope.$broadcast(broadcastMessage+'Finished');
});
}); });
} }
} }
@ -1267,22 +1387,29 @@ angular.module('forms').directive('onFinishRender', function ($rootScope, $timeo
'use strict'; 'use strict';
angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCounter', 'Auth', angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCounter', 'Auth', '$filter',
function ($http, $timeout, timeCounter, Auth) { function ($http, $timeout, timeCounter, Auth, $filter) {
return { return {
templateUrl: './modules/forms/views/directiveViews/form/submit-form.html',
restrict: 'E',
scope: {
form:'='
},
controller: function($scope){ controller: function($scope){
console.log('rendering submitFormDirective');
timeCounter.startClock(); timeCounter.startClock();
$scope.submit = function(){ $scope.submit = function(){
var _timeElapsed = timeCounter.stopClock(); var _timeElapsed = timeCounter.stopClock();
$scope.form.timeElapsed = _timeElapsed; $scope.form.timeElapsed = _timeElapsed;
// console.log($scope.form.timeElapsed); $scope.form.percentageComplete = $filter('formValidity')($scope.form.visible_form_fields)/$scope.visible_form_fields.length;
$scope.authentication = Auth; delete $scope.form.visible_form_fields;
console.log($scope.authentication.isAuthenticated());
$scope.submitPromise = $http.post('/forms/'+$scope.form._id,$scope.form). $scope.authentication = Auth;
success(function(data, status, headers){
$scope.submitPromise = $http.post('/forms/'+$scope.form._id,$scope.form)
.success(function(data, status, headers){
console.log('form submitted successfully'); console.log('form submitted successfully');
// alert('Form submitted..'); // alert('Form submitted..');
$scope.form.submitted = true; $scope.form.submitted = true;
@ -1294,18 +1421,15 @@ angular.module('forms').directive('formDirective', ['$http', '$timeout', 'timeCo
}; };
$scope.reloadForm = function(){ $scope.reloadForm = function(){
timeCounter.stopClock();
timeCounter.startClock();
$scope.form.submitted = false; $scope.form.submitted = false;
$scope.form.form_fields = _.chain($scope.form.form_fields).map(function(field){ $scope.form.form_fields = _.chain($scope.form.form_fields).map(function(field){
field.fieldValue = ''; field.fieldValue = '';
return field; return field;
}).value(); }).value();
} };
},
templateUrl: './modules/forms/views/directiveViews/form/submit-form.html',
restrict: 'E',
scope: {
form:'='
} }
}; };
} }
@ -1378,7 +1502,7 @@ angular.module('forms').service('FormFields', [
}, },
{ {
name : 'dropdown', name : 'dropdown',
value : 'Dropdown List' value : 'Dropdown'
}, },
{ {
name : 'date', name : 'date',
@ -1386,7 +1510,7 @@ angular.module('forms').service('FormFields', [
}, },
{ {
name : 'textarea', name : 'textarea',
value : 'Long Text' value : 'Paragraph Text'
}, },
{ {
name : 'checkbox', name : 'checkbox',
@ -1394,13 +1518,17 @@ angular.module('forms').service('FormFields', [
}, },
{ {
name : 'yes_no', name : 'yes_no',
value : 'Yes or No' value : 'Yes/No'
}, },
{ {
name : 'legal', name : 'legal',
value : 'Legal' value : 'Legal'
}, },
// { // {
// name : 'sig',
// value : 'Signature'
// },
// {
// name : 'file', // name : 'file',
// value : 'File Upload' // value : 'File Upload'
// }, // },
@ -1424,6 +1552,10 @@ angular.module('forms').service('FormFields', [
name : 'statement', name : 'statement',
value : 'Statement' value : 'Statement'
}, },
{
name : 'natural',
value : 'Natural Language Input'
},
]; ];
} }
@ -1864,9 +1996,9 @@ angular.module('users').factory('Auth', function($window) {
// because that would create a circular dependency // because that would create a circular dependency
// Auth <- $http <- $resource <- LoopBackResource <- User <- Auth // Auth <- $http <- $resource <- LoopBackResource <- User <- Auth
ensureHasCurrentUser: function(User) { ensureHasCurrentUser: function(User) {
if (service.currentUser && service.currentUser.displayName) { if (service.currentUser && service.currentUser.username) {
console.log('Using local current user.'); console.log('Using local current user.');
console.log(service.currentUser); // console.log(service.currentUser);
return service.currentUser; return service.currentUser;
} }
else if ($window.user){ else if ($window.user){

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -197,27 +197,30 @@ angular.module('forms').controller('ViewFormController', ['$rootScope', '$scope'
// Update existing Form // Update existing Form
$scope.update = $rootScope.update = function(cb) { $scope.update = $rootScope.update = function(immediate, cb) {
if(!$rootScope.saveInProgress && $rootScope.finishedRender){ console.log('immediate: '+immediate);
var continueUpdate = true;
if(immediate){
continueUpdate = !$rootScope.saveInProgress;
}
$rootScope.saveInProgress = true; if(continueUpdate){
console.log('begin updating form'); console.log('begin updating form');
var err = null; var err = null;
if(immediate){ $rootScope.saveInProgress = true; }
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, {form: $scope.myform}) $scope.updatePromise = $http.put('/forms/'+$scope.myform._id, {form: $scope.myform})
.then(function(response){ .then(function(response){
$rootScope.myform = $scope.myform = response.data; $rootScope.myform = $scope.myform = response.data;
console.log(response.data); console.log(response.data);
if(!$scope.$digest){
$scope.$apply();
}
}).catch(function(response){ }).catch(function(response){
console.log('Error occured during form UPDATE.\n'); console.log('Error occured during form UPDATE.\n');
console.log(response.data); console.log(response.data);
err = response.data; err = response.data;
}).finally(function() { }).finally(function() {
console.log('finished updating'); console.log('finished updating');
$rootScope.saveInProgress = false; if(immediate){$rootScope.saveInProgress = false; }
cb(err); cb(err);
}); });
} }

View File

@ -161,7 +161,8 @@ div.config-form > .row {
margin-bottom: 40px; margin-bottom: 40px;
} }
.admin-form > .page-header h1 { .admin-form > .page-header h1 {
margin-bottom: 0px margin-bottom: 0px;
margin-top: 0px;
} }
.admin-form > .page-header > .col-xs-3 { .admin-form > .page-header > .col-xs-3 {
padding-top: 1.4em; padding-top: 1.4em;

View File

@ -4,14 +4,17 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return { return {
require: ['^form'], require: ['^form'],
link: function($scope, $element, $attrs, $ctrls) { restrict: 'AE',
controller: function ($scope) {
$rootScope.finishedRender = false; },
link: function($scope, $element, $attrs, $ctrls) {
angular.element(document).ready(function() {
var $formCtrl = $ctrls[0], var $formCtrl = $ctrls[0],
savePromise = null; savePromise = null;
$rootScope.finishedRender = false;
$scope.$on('editFormFieldsStarted', function(ngRepeatFinishedEvent) { $scope.$on('editFormFieldsStarted', function(ngRepeatFinishedEvent) {
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
}); });
@ -19,59 +22,38 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
$rootScope.finishedRender = true; $rootScope.finishedRender = true;
}); });
// $scope.anyDirtyAndTouched = function (form){ $scope.anyDirtyAndTouched = function(form){
// console.log(form); var propCount = 0;
// console.log($scope.myform); for(var prop in form) {
if(form.hasOwnProperty(prop) && prop[0] !== '$') {
propCount++;
// for(var prop in form) { if(form[prop].$touched && form[prop].$dirty) {
// if(form.hasOwnProperty(prop) && prop[0] !== '$') { return true;
// if(form[prop].$dirty && form[prop].$touched) {
// return true;
// }
// }
// }
// return false;
// };
// console.log($scope.watchModel);
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
console.log('hello');
if( !newValue && !oldValue ){
return;
} }
var changedFields = !_.isEqual(oldValue,newValue); }
}
return false;
};
// console.log('\n\n----------'); var debounceUpdates = function () {
console.log('$dirty: '+( $formCtrl.$dirty ) );
// console.log('oldValue: '+oldValue);
// console.log('newValue: '+newValue);
// console.log('form_fields changed: '+changedFields);
// console.log('$valid: '+$formCtrl.$valid);
// console.log('finishedRender: '+$rootScope.finishedRender);
// console.log('saveInProgress: '+$rootScope.saveInProgress);
// var inputDirtyUntouched = $scope.anyDirtyAndTouched($scope.editForm); // console.log('Saving Form');
//Save form ONLY IF rendering is finished, form_fields have been change AND currently not save in progress
if($rootScope.finishedRender && (inputDirtyUntouched || changedFields) && !$rootScope.saveInProgress) {
console.log('Saving Form');
if(savePromise) { if(savePromise) {
$timeout.cancel(savePromise); $timeout.cancel(savePromise);
} }else {
savePromise = $timeout(function() { savePromise = $timeout(function() {
savePromise = null;
$rootScope[$attrs.autoSaveCallback]( $rootScope[$attrs.autoSaveCallback](
function(err){ function(err){
if(!err){ if(!err){
console.log('\n\nForm data persisted -- setting pristine flag'); // console.log('\n\nForm data persisted -- setting pristine flag');
// console.log('\n\n---------\nUpdate form CLIENT'); // console.log('\n\n---------\nUpdate form CLIENT');
// console.log(Date.now()); // console.log(Date.now());
$formCtrl.$setPristine(); $formCtrl.$setPristine();
// $rootScope.saveInProgress = false; console.log($rootScope.saveInProgress);
savePromise = null;
// $formCtrl.$setUntouched();
}else{ }else{
console.error('Error form data NOT persisted'); console.error('Error form data NOT persisted');
console.error(err); console.error(err);
@ -79,13 +61,96 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
}); });
}); });
}
};
// $scope.$watch('editForm', function(newValue, oldValue) {
// console.log('watch editForm');
// if($scope.anyDirtyAndTouched($scope.editForm)){
// console.log('ready to save text input');
// } // }
// });
$scope.$watch(function(newValue, oldValue) {
// console.log('watch editForm');
if($scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
console.log('ready to save text input');
console.log('Saving Form');
$rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](false,
function(err){
if(!err){
console.log('\n\nForm data persisted -- setting pristine flag');
// console.log('\n\n---------\nUpdate form CLIENT');
// console.log(Date.now());
$formCtrl.$setPristine();
}else{ }else{
console.error('Error form data NOT persisted');
console.error(err);
}
});
}
});
//Autosave Form when model (specificed in $attrs.autoSaveWatch) changes
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
var changedFields = !_.isEqual(oldValue,newValue);
if( (!newValue && !oldValue) || !oldValue ){
return; return;
} }
// console.log('\n\n----------');
// console.log('$dirty: '+ $formCtrl.$dirty );
// console.log('changedFields: '+changedFields);
// console.log('finishedRender: '+$rootScope.finishedRender);
// console.log('saveInProgress: '+$rootScope.saveInProgress);
// console.log('newValue: '+newValue);
// console.log('oldValue: '+oldValue);
// var anyCurrentlyDirtyNotFocused = $scope.anyDirtyAndTouched($scope.editForm);
// console.log('anyCurrentlyDirtyNotFocused: '+anyCurrentlyDirtyNotFocused);
// console.log('properties of $scope.editForm');
// for(var item in $scope.editForm){
// console.log(item);
// }
//Save form ONLY IF rendering is finished, form_fields have been change AND currently not save in progress
if($rootScope.finishedRender && (changedFields && !$formCtrl.$dirty) && !$rootScope.saveInProgress) {
if(savePromise) {
$timeout.cancel(savePromise);
savePromise = null;
}
savePromise = $timeout(function() {
console.log('Saving Form');
$rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](false,
function(err){
if(!err){
console.log('\n\nForm data persisted -- setting pristine flag');
// console.log('\n\n---------\nUpdate form CLIENT');
// console.log(Date.now());
$formCtrl.$setPristine();
}else{
console.error('Error form data NOT persisted');
console.error(err);
}
});
});
}else if($rootScope.finishedRender && $rootScope.saveInProgress){
$rootScope.saveInProgress = false;
}
}, true); }, true);
});
} }
}; };

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$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) { function ($rootScope, $q, $http, $timeout, timeCounter, Auth, FormFields) {
return { return {
templateUrl: './modules/forms/views/directiveViews/form/edit-form.html', templateUrl: './modules/forms/views/directiveViews/form/edit-form.html',
@ -8,8 +9,28 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
scope: { scope: {
myform:'=', myform:'=',
}, },
transclude: true, // transclude: true,
controller: function($scope){ controller: function($scope){
// Log that the directive has been linked.
// console.log( "Linked: editForm Controller");
/*
** Initialize scope with variables
*/
//Populate AddField with all available form field types
$scope.addField = {};
$scope.addField.types = FormFields.fields;
$scope.addField.types.forEach(function(type){
type.lastAddedID = 1;
return type;
});
// accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
//Populate local scope with rootScope methods/variables //Populate local scope with rootScope methods/variables
$scope.update = $rootScope.update; $scope.update = $rootScope.update;
@ -67,19 +88,9 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
// }; // };
//Populate AddField with all available form field types /*
$scope.addField = {}; ** Field CRUD Methods
$scope.addField.types = FormFields.fields; */
$scope.addField.types.forEach(function(type){
type.lastAddedID = 1;
return type;
});
// accordion settings
$scope.accordion = {};
$scope.accordion.oneAtATime = true;
// Add a new field // Add a new field
$scope.addNewField = function(fieldType){ $scope.addNewField = function(fieldType){
@ -105,7 +116,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
}; };
// put newField into fields array // put newField into fields array
$scope.myform.form_fields.unshift(newField); $scope.myform.form_fields.push(newField);
// console.log('\n\n---------\nAdded field CLIENT'); // console.log('\n\n---------\nAdded field CLIENT');
// console.log(Date.now()); // console.log(Date.now());
// console.log($scope.myform.form_fields.length); // console.log($scope.myform.form_fields.length);
@ -131,6 +142,10 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', '$q', '$ht
} }
}; };
/*
** Field Option Methods
*/
// add new option to the field // add new option to the field
$scope.addOption = function (field){ $scope.addOption = function (field){
if(!field.fieldOptions) field.fieldOptions = []; if(!field.fieldOptions) field.fieldOptions = [];

View File

@ -12,8 +12,7 @@ angular.module('forms').directive('onFinishRender', function ($rootScope, $timeo
var broadcastMessage = attrs.onFinishRender || 'ngRepeat'; var broadcastMessage = attrs.onFinishRender || 'ngRepeat';
if(!scope.$last) {
if(scope.$first && !scope.$last) {
$timeout(function () { $timeout(function () {
// console.log(broadcastMessage+'Started'); // console.log(broadcastMessage+'Started');
$rootScope.$broadcast(broadcastMessage+'Started'); $rootScope.$broadcast(broadcastMessage+'Started');

View File

@ -0,0 +1,3 @@
<div>
<div style="text-align:center;font-size:26px;position:absolute;top:100px;width:100%;text-shadow:1px 1px 2px white, -1px -1px 2px white,-4px 4px 4px white,-4px 4px 4px white">{{$message}}</div>
</div>

View File

@ -183,7 +183,7 @@
<button class="btn btn-primary btn-large" type="button" ng-click="update()"><i class="icon-arrow-left icon-white"></i> Save Changes</button> <button class="btn btn-primary btn-large" type="button" ng-click="update()"><i class="icon-arrow-left icon-white"></i> Save Changes</button>
</div> </div>
<div class="col-sm-1"> <div class="col-sm-1">
<button class="btn btn-primary" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i> Cancel</button> <button class="btn btn-default" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i> Cancel</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
<form class="row" name="editForm" auto-save-form auto-save-watch="myform.form_fields" auto-save-callback="update" novalidation>
<div class="col-xs-5 add-field"> <form class="row" name="editForm" auto-save-form auto-save-watch="myform.form_fields" auto-save-callback="update">
<div class="col-xs-5 add-field" id="hello">
<!-- <select ng-model="addField.new" ng-options="type.name as type.value for type in addField.types"></select> <!-- <select ng-model="addField.new" ng-options="type.name as type.value for type in addField.types"></select>
<button type="submit" class="btn" ng-click="addNewField()"> <button type="submit" class="btn" ng-click="addNewField()">
<i class="icon-plus"></i> Add Field <i class="icon-plus"></i> Add Field
@ -47,12 +48,11 @@
</div> </div>
</accordion-heading> </accordion-heading>
<div class="accordion-edit container"> <div class="accordion-edit container">
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<h4>Preview</h4> <h4>Preview Field</h4>
</div> </div>
</div>
<div class="row">
<ul class="col-xs-12 container" style="list-style:none;border:2px lightgray solid;"> <ul class="col-xs-12 container" style="list-style:none;border:2px lightgray solid;">
<field-directive field="field" validate="false"> <field-directive field="field" validate="false">
</field-directive> </field-directive>
@ -62,18 +62,19 @@
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<hr> <hr>
<h4>Edit</h4> <h4>Edit Field</h4>
<br> <br>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-4">Question Title:</div> <div class="col-xs-4">Question Title:</div>
<div class="col-xs-8"> <div class="col-xs-8">
<input type="text" ng-model="field.title" name="title{{field._id}}" value="{{field.title}}" required></div> <input type="text" ng-model="field.title" name="title{{field._id}}" value="{{field.title}}" required></div>
</div> </div>
<br>
<div class="row"><br></div>
<div class="row"> <div class="row">
<div class="col-xs-4">Description:</div> <div class="col-xs-4">Description:</div>
<div class="col-xs-8"><textarea type="text" ng-model="field.description" name="description{{field._id}}"value="{{field.description}}"></textarea> </div> <div class="col-xs-8"><textarea type="text" ng-model="field.description" name="description{{field._id}}"value="{{field.description}}"></textarea> </div>
@ -108,28 +109,38 @@
</label> </label>
<label class="btn col-xs-5 col-xs-offset-1"> <label class="btn col-xs-5 col-xs-offset-1">
<input type="radio" ng-value="false" ng-model="field.required" name="required{{field._id}}"/>/> <input type="radio" ng-value="false" ng-model="field.required" name="required{{field._id}}"/>
<span> &nbsp; No</span> <span> &nbsp; No</span>
</label> </label>
</div> </div>
</div> </div>
<br>
<div class="row"> <div class="row">
<div class="col-xs-4 field-input">Disabled:</div> <div class="col-xs-4 field-input">Disabled:</div>
<div class="col-xs-8 field-input"> <div class="col-xs-8 field-input">
<label class="btn col-xs-5"> <label class="btn col-xs-5">
<input type="radio" ng-value="true" <input type="radio" ng-value="true"
ng-model="field.disabled" name="disabled{{field._id}}"/>/> ng-model="field.disabled" name="disabled{{field._id}}"/>
<span> &nbsp; Yes</span> <span> &nbsp; Yes</span>
</label> </label>
<label class="btn col-xs-5 col-xs-offset-1"> <label class="btn col-xs-5 col-xs-offset-1">
<input type="radio" ng-value="false" <input type="radio" ng-value="false"
ng-model="field.disabled" name="disabled{{field._id}}"/>/> ng-model="field.disabled" name="disabled{{field._id}}"/>
<span> &nbsp; No</span> <span> &nbsp; No</span>
</label> </label>
</div> </div>
</div> </div>
<div class="row"><br><hr></div>
<div class="row">
<div class="col-sm-4 col-sm-offset-4">
<button class="btn btn-primary btn-large" type="button" ng-click="update()">
<i class="icon-arrow-left icon-white"></i> Save Changes
</button>
</div>
</div>
</div> </div>
</accordion-group> </accordion-group>
@ -167,4 +178,5 @@
</div> </div>
</div> </div>
</div> </div>
</form>
</form>

View File

@ -23,10 +23,10 @@
</script> </script>
<div class="page-header row" style="padding-bottom: 0px;"> <div class="page-header row" style="padding-bottom: 0px;">
<div class="col-xs-6"> <div class="col-xs-8">
<h1 data-ng-bind="myform.title" style="margin-bottom: 0px;"></h1> <h1 data-ng-bind="myform.title" style="margin-bottom: 0px;"></h1>
</div> </div>
<div class="col-xs-2 col-xs-offset-2"> <div class="col-xs-2">
<small class=" pull-right"> <small class=" pull-right">
<button class="btn btn-danger" ng-click="openDeleteModal()"><i class="fa fa-trash-o"></i> Delete Form</button> <button class="btn btn-danger" ng-click="openDeleteModal()"><i class="fa fa-trash-o"></i> Delete Form</button>
</small> </small>
@ -40,7 +40,6 @@
<span ng-hide="myform.isLive">Preview</span> Form <span ng-hide="myform.isLive">Preview</span> Form
<i class="status-light status-light-on fa fa-dot-circle-o" ng-show="myform.isLive"></i> <i class="status-light status-light-on fa fa-dot-circle-o" ng-show="myform.isLive"></i>
<i class="status-light status-light-off fa fa-dot-circle-o" ng-hide="myform.isLive"></i> <i class="status-light status-light-off fa fa-dot-circle-o" ng-hide="myform.isLive"></i>
<!-- <i class="fa fa-sign-out"></i> -->
</a> </a>
</small> </small>
</div> </div>
@ -59,13 +58,11 @@
<tab-heading > <tab-heading >
Edit Design Edit Design
</tab-heading> </tab-heading>
<div cg-busy="{promise:updatePromise}"></div>
</tab> </tab>
<tab> <tab>
<tab-heading> <tab-heading>
Configure Configure
</tab-heading> </tab-heading>
<div cg-busy="{promise:updatePromise}"></div>
<configure-form-directive myform="myform" user="user"> <configure-form-directive myform="myform" user="user">
</configure-form-directive> </configure-form-directive>
</tab> </tab>