added diff to update

This commit is contained in:
David Baldwynn 2016-08-26 15:34:29 -07:00
commit 0a74598894
13 changed files with 311 additions and 287 deletions

View file

@ -12,6 +12,7 @@ var mongoose = require('mongoose'),
fs = require('fs-extra'), fs = require('fs-extra'),
async = require('async'), async = require('async'),
path = require('path'), path = require('path'),
diff = require('deep-diff'),
_ = require('lodash'); _ = require('lodash');
/** /**
@ -69,74 +70,6 @@ exports.uploadPDF = function(req, res, next) {
} }
}; };
/**
* Upload PDF
*/
/*
exports.uploadSubmissionFile = function(req, res, next) {
console.log('inside uploadPDF');
// console.log('\n\nProperty Descriptor\n-----------');
// console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path'));
console.log(req.files);
if(req.files){
var file, _user, _path;
for(var i=0; i<req.files.length; i++){
file = req.files[i];
_user = req.user;
_path = file.path;
if (file.size === 0) {
return next(new Error('File uploaded is EMPTY'));
}else if(file.size > 100000000){
return next(new Error('File uploaded exceeds MAX SIZE of 100MB'));
}else {
fs.exists(_path, function(exists) {
//If file exists move to user's form directory
if(exists) {
var newDestination = config.tmpUploadPath+_user.username;
var stat = null;
try {
stat = fs.statSync(newDestination);
} catch (err) {
fs.mkdirSync(newDestination);
}
if (stat && !stat.isDirectory()) {
console.log('Directory cannot be created');
return next(new Error('Directory cannot be created because an inode of a different type exists at "' + newDestination + '"'));
}
console.log(path.join(newDestination, pdfFile.filename));
fs.move(pdfFile.path, path.join(newDestination, pdfFile.filename), function (err) {
if (err) {
return next(new Error(err.message));
}
pdfFile.path = path.join(newDestination, pdfFile.filename);
console.log(pdfFile.filename + ' uploaded to ' + pdfFile.path);
res.json(pdfFile);
});
} else {
return next(new Error('Did NOT get your file!'));
}
});
}
}
}else {
return next(new Error('Uploaded files were NOT detected'));
}
};
*/
/** /**
* Delete a forms submissions * Delete a forms submissions
*/ */
@ -294,7 +227,7 @@ exports.read = function(req, res) {
}); });
} }
return res.json(newForm); return res.json(newForm);
}; };
/** /**
@ -302,22 +235,32 @@ exports.read = function(req, res) {
*/ */
exports.update = function(req, res) { exports.update = function(req, res) {
var form = req.form; var form = req.form;
/*
delete req.body.form.__v; delete req.body.form.__v;
delete req.body.form._id; delete req.body.form._id;
*/
//Unless we have 'admin' priviledges, updating form admin is disabled if(req.body.changes){
if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin; var formChanges = req.body.changes;
//Do this so we can create duplicate fields formChanges.forEach(function (change) {
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$'); diff.applyChange(form, true, change);
for(var i=0; i<req.body.form.form_fields.length; i++){ });
var field = req.body.form.form_fields[i]; } else {
if(!checkForValidId.exec(field._id+'')){ //Unless we have 'admin' privileges, updating form admin is disabled
delete field._id; if(req.user.roles.indexOf('admin') === -1) delete req.body.form.admin;
//Do this so we can create duplicate fields
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
for(var i=0; i<req.body.form.form_fields.length; i++){
var field = req.body.form.form_fields[i];
if(!checkForValidId.exec(field._id+'')){
delete field._id;
}
} }
form = _.extend(form, req.body.form);
} }
form = _.extend(form, req.body.form);
form.save(function(err, form) { form.save(function(err, form) {
if (err) { if (err) {

View file

@ -36,8 +36,9 @@
"js-yaml": "^3.6.1", "js-yaml": "^3.6.1",
"angular-ui-select": "https://github.com/whitef0x0/ui-select.git#compiled", "angular-ui-select": "https://github.com/whitef0x0/ui-select.git#compiled",
"angular-translate": "~2.11.0", "angular-translate": "~2.11.0",
"ng-device-detector": "~3.0.1", "ng-device-detector": "^3.0.1",
"ng-translate": "*" "ng-translate": "*",
"deep-diff": "^0.3.4"
}, },
"resolutions": { "resolutions": {
"angular-bootstrap": "^0.14.0", "angular-bootstrap": "^0.14.0",

View file

@ -35,6 +35,7 @@
"connect-mongo": "~0.8.2", "connect-mongo": "~0.8.2",
"consolidate": "~0.13.1", "consolidate": "~0.13.1",
"cookie-parser": "~1.4.0", "cookie-parser": "~1.4.0",
"deep-diff": "^0.3.4",
"dotenv": "^2.0.0", "dotenv": "^2.0.0",
"email-verification": "~0.4.1", "email-verification": "~0.4.1",
"envfile": "^2.0.1", "envfile": "^2.0.1",

View file

@ -326,7 +326,7 @@ angular.module('forms').config(['$translateProvider', function ($translateProvid
SUBMIT: 'Submit', SUBMIT: 'Submit',
UPLOAD_FILE: 'Upload your File' UPLOAD_FILE: 'Upload your File'
}); });
}]); }]);
'use strict'; 'use strict';
@ -1268,7 +1268,7 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$loca
angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$state', 'User', angular.module('users').controller('PasswordController', ['$scope', '$stateParams', '$state', 'User',
function($scope, $stateParams, $state, User) { function($scope, $stateParams, $state, User) {
$scope.error = ''; $scope.error = '';
// Submit forgotten password account id // Submit forgotten password account id
$scope.askForPasswordReset = function() { $scope.askForPasswordReset = function() {
User.askForPasswordReset($scope.credentials).then( User.askForPasswordReset($scope.credentials).then(
@ -1309,7 +1309,7 @@ angular.module('users').controller('SettingsController', ['$scope', '$rootScope'
function($scope, $rootScope, $http, $state, Users) { function($scope, $rootScope, $http, $state, Users) {
$scope.user = $rootScope.user; $scope.user = $rootScope.user;
// Check if there are additional accounts // Check if there are additional accounts
$scope.hasConnectedAdditionalSocialAccounts = function(provider) { $scope.hasConnectedAdditionalSocialAccounts = function(provider) {
for (var i in $scope.user.additionalProvidersData) { for (var i in $scope.user.additionalProvidersData) {
return true; return true;
@ -1788,7 +1788,7 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
}; };
// Update existing Form // Update existing Form
$scope.update = $rootScope.update = function(updateImmediately, cb){ $scope.update = $rootScope.update = function(updateImmediately, diffChanges, cb){
var continueUpdate = true; var continueUpdate = true;
if(!updateImmediately){ if(!updateImmediately){
@ -1801,7 +1801,9 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
if(!updateImmediately){ $rootScope.saveInProgress = true; } if(!updateImmediately){ $rootScope.saveInProgress = true; }
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, {form: $scope.myform}) console.log(diffChanges);
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, { changes: diffChanges })
.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);
@ -1954,7 +1956,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
$scope.$on('editFormFields Started', function(ngRepeatFinishedEvent) { $scope.$on('editFormFields Started', function(ngRepeatFinishedEvent) {
// console.log('hello');
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
}); });
$scope.$on('editFormFields Finished', function(ngRepeatFinishedEvent) { $scope.$on('editFormFields Finished', function(ngRepeatFinishedEvent) {
@ -1974,13 +1975,11 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return false; return false;
}; };
var debounceSave = function () { var debounceSave = function (diffChanges) {
$rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](true, $rootScope[$attrs.autoSaveCallback](true, diffChanges,
function(err){ function(err){
if(!err){ if(!err){
//console.log('\n\nForm data persisted -- setting pristine flag');
$formCtrl.$setPristine(); $formCtrl.$setPristine();
$formCtrl.$setUntouched(); $formCtrl.$setUntouched();
}else{ }else{
@ -1992,12 +1991,10 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
//Update/Save Form if any Form fields are Dirty and Touched //Update/Save Form if any Form fields are Dirty and Touched
$scope.$watch(function(newValue, oldValue) { $scope.$watch(function(newValue, oldValue) {
//console.log('introParagraphStartPage.$dirty: '+$scope.editForm.introParagraphStartPage.$dirty);
//console.log('introParagraphStartPage.$touched: '+$scope.editForm.introParagraphStartPage.$touched);
if($rootScope.finishedRender && $scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){ if($rootScope.finishedRender && $scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
//console.log('Form saving started'); delete newValue.visible_form_fields;
debounceSave(); debounceSave(DeepDiff.diff(oldValue, newValue));
//console.log('introParagraphStartPage.$dirty AFTER: '+$scope.editForm.introParagraphStartPage.$dirty);
} }
}); });
@ -2011,7 +2008,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
oldValue.form_fields = _.removeDateFields(oldValue.form_fields); oldValue.form_fields = _.removeDateFields(oldValue.form_fields);
var changedFields = !_.isEqual(oldValue.form_fields,newValue.form_fields) || !_.isEqual(oldValue.startPage, newValue.startPage); var changedFields = !_.isEqual(oldValue.form_fields,newValue.form_fields) || !_.isEqual(oldValue.startPage, newValue.startPage);
var changedFieldMap = false;
if(oldValue.hasOwnProperty('plugins.oscarhost.settings.fieldMap')){ if(oldValue.hasOwnProperty('plugins.oscarhost.settings.fieldMap')){
changedFieldMap = !!oldValue.plugins.oscarhost.settings.fieldMap && !_.isEqual(oldValue.plugins.oscarhost.settings.fieldMap,newValue.plugins.oscarhost.settings.fieldMap); changedFieldMap = !!oldValue.plugins.oscarhost.settings.fieldMap && !_.isEqual(oldValue.plugins.oscarhost.settings.fieldMap,newValue.plugins.oscarhost.settings.fieldMap);
@ -2039,7 +2035,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
} }
//Save form ONLY IF rendering is finished, form_fields have been changed AND currently not save in progress //Save form ONLY IF rendering is finished, form_fields have been changed AND currently not save in progress
if( $rootScope.finishedRender && ((changedFields && !$formCtrl.$dirty) || changedFieldMap) && !$rootScope.saveInProgress) { if( $rootScope.finishedRender && (changedFields && !$formCtrl.$dirty) && !$rootScope.saveInProgress) {
if(savePromise) { if(savePromise) {
$timeout.cancel(savePromise); $timeout.cancel(savePromise);
@ -2047,7 +2043,14 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
} }
savePromise = $timeout(function() { savePromise = $timeout(function() {
debounceSave(); $rootScope.saveInProgress = true;
console.log(newValue);
console.log(oldValue);
delete newValue.visible_form_fields;
delete newValue.visible_form_fields;
var _diff = DeepDiff.diff(oldValue, newValue);
debounceSave(_diff);
}); });
} }
//If we are finished rendering then form saving should be finished //If we are finished rendering then form saving should be finished
@ -2169,7 +2172,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}, },
controller: ["$scope", function($scope){ controller: ["$scope", function($scope){
console.log($scope.myform);
var field_ids = _($scope.myform.form_fields).pluck('_id'); var field_ids = _($scope.myform.form_fields).pluck('_id');
for(var i=0; i<field_ids.length; i++){ for(var i=0; i<field_ids.length; i++){
$scope.myform.plugins.oscarhost.settings.fieldMap[field_ids[i]] = null; $scope.myform.plugins.oscarhost.settings.fieldMap[field_ids[i]] = null;
@ -2268,7 +2270,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
} }
} }
var newField = { var newField = {
title: fieldTitle, title: fieldTitle + ' ' + $scope.myform.form_fields.length+1,
fieldType: fieldType, fieldType: fieldType,
fieldValue: '', fieldValue: '',
required: true, required: true,
@ -2285,7 +2287,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}); });
} }
if(modifyForm){ if(modifyForm){
//Add newField to form_fields array //Add newField to form_fields array
$scope.myform.form_fields.push(newField); $scope.myform.form_fields.push(newField);
@ -2656,15 +2657,15 @@ angular.module('forms').service('FormFields', [
// }, // },
// { // {
// name : 'stripe', // name : 'stripe',
// value : 'Payment' // value : 'Payment'
// }, // },
{ {
name : 'statement', name : 'statement',
value : 'Statement' value : 'Statement'
} }
]; ];
} }
]); ]);
'use strict'; 'use strict';
@ -2677,7 +2678,7 @@ angular.module('forms').factory('Submissions', ['$resource',
formId: '@_id' formId: '@_id'
}, { }, {
'query' : { 'query' : {
method: 'GET', method: 'GET',
isArray: true, isArray: true,
}, },
'update': { 'update': {
@ -2854,23 +2855,25 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){ if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
switch(scope.field.fieldType){ switch(scope.field.fieldType){
case 'textfield': case 'textfield':
scope.field.input_type = 'text'; scope.input_type = 'text';
break; break;
case 'email': case 'email':
scope.field.input_type = 'email'; scope.input_type = 'email';
scope.field.placeholder = 'joesmith@example.com'; scope.placeholder = 'joesmith@example.com';
break; break;
case 'number': case 'number':
scope.field.input_type = 'text'; scope.input_type = 'text';
scope.field.validateRegex = /^-?\d+$/; scope.validateRegex = /^-?\d+$/;
break; break;
default: default:
scope.field.input_type = 'url'; scope.input_type = 'url';
scope.field.placeholder = 'http://example.com'; scope.placeholder = 'http://example.com';
break; break;
} }
fieldType = 'textfield'; fieldType = 'textfield';
} }
console.log(scope.input_type);
var template = getTemplateUrl(fieldType); var template = getTemplateUrl(fieldType);
element.html(template).show(); element.html(template).show();
var output = $compile(element.contents())(scope); var output = $compile(element.contents())(scope);
@ -2961,7 +2964,7 @@ angular.module('forms').directive('onFinishRender', ["$rootScope", "$timeout", f
return { return {
restrict: 'A', restrict: 'A',
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
//Don't do anything if we don't have a ng-repeat on the current element //Don't do anything if we don't have a ng-repeat on the current element
if(!element.attr('ng-repeat') && !element.attr('data-ng-repeat')){ if(!element.attr('ng-repeat') && !element.attr('data-ng-repeat')){
return; return;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2,107 +2,108 @@
// coffeescript's for in loop // coffeescript's for in loop
var __indexOf = [].indexOf || function(item) { var __indexOf = [].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) { for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item) return i; if (i in this && this[i] === item) return i;
} }
return -1; return -1;
}; };
angular.module('view-form').directive('fieldDirective', ['$http', '$compile', '$rootScope', '$templateCache', 'supportedFields', angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$rootScope', '$templateCache', 'supportedFields',
function($http, $compile, $rootScope, $templateCache, supportedFields) { function($http, $compile, $rootScope, $templateCache, supportedFields) {
var getTemplateUrl = function(fieldType) { var getTemplateUrl = function(fieldType) {
var type = fieldType; var type = fieldType;
var supported_fields = [ var supported_fields = [
'textfield', 'textfield',
'textarea', 'textarea',
'date', 'date',
'dropdown', 'dropdown',
'hidden', 'hidden',
'password', 'password',
'radio', 'radio',
'legal', 'legal',
'statement', 'statement',
'rating', 'rating',
'yes_no', 'yes_no',
'number', 'number',
'natural' 'natural'
]; ];
var templateUrl = 'modules/forms/base/views/directiveViews/field/'; var templateUrl = 'modules/forms/base/views/directiveViews/field/';
if (__indexOf.call(supportedFields, type) >= 0) { if (__indexOf.call(supportedFields, type) >= 0) {
templateUrl = templateUrl+type+'.html'; templateUrl = templateUrl+type+'.html';
}
return $templateCache.get(templateUrl);
};
return {
template: '<div>{{field.title}}</div>',
restrict: 'E',
scope: {
field: '=',
required: '&',
design: '=',
index: '=',
forms: '='
},
link: function(scope, element) {
$rootScope.chooseDefaultOption = scope.chooseDefaultOption = function(type) {
if(type === 'yes_no'){
scope.field.fieldValue = 'true';
}else if(type === 'rating'){
scope.field.fieldValue = 0;
}else if(scope.field.fieldType === 'radio'){
console.log(scope.field);
scope.field.fieldValue = scope.field.fieldOptions[0].option_value;
console.log(scope.field.fieldValue);
}else if(type === 'legal'){
scope.field.fieldValue = 'true';
$rootScope.nextField();
}
};
scope.setActiveField = $rootScope.setActiveField;
//Set format only if field is a date
if(scope.field.fieldType === 'date'){
scope.dateOptions = {
changeYear: true,
changeMonth: true,
altFormat: 'mm/dd/yyyy',
yearRange: '1900:-0',
defaultDate: 0
};
}
var fieldType = scope.field.fieldType;
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
switch(scope.field.fieldType){
case 'textfield':
scope.field.input_type = 'text';
break;
case 'email':
scope.field.input_type = 'email';
scope.field.placeholder = 'joesmith@example.com';
break;
case 'number':
scope.field.input_type = 'text';
scope.field.validateRegex = /^-?\d+$/;
break;
default:
scope.field.input_type = 'url';
scope.field.placeholder = 'http://example.com';
break;
}
fieldType = 'textfield';
} }
var template = getTemplateUrl(fieldType); return $templateCache.get(templateUrl);
element.html(template).show(); };
var output = $compile(element.contents())(scope);
} return {
}; template: '<div>{{field.title}}</div>',
}]); restrict: 'E',
scope: {
field: '=',
required: '&',
design: '=',
index: '=',
forms: '='
},
link: function(scope, element) {
$rootScope.chooseDefaultOption = scope.chooseDefaultOption = function(type) {
if(type === 'yes_no'){
scope.field.fieldValue = 'true';
}else if(type === 'rating'){
scope.field.fieldValue = 0;
}else if(scope.field.fieldType === 'radio'){
console.log(scope.field);
scope.field.fieldValue = scope.field.fieldOptions[0].option_value;
console.log(scope.field.fieldValue);
}else if(type === 'legal'){
scope.field.fieldValue = 'true';
$rootScope.nextField();
}
};
scope.setActiveField = $rootScope.setActiveField;
//Set format only if field is a date
if(scope.field.fieldType === 'date'){
scope.dateOptions = {
changeYear: true,
changeMonth: true,
altFormat: 'mm/dd/yyyy',
yearRange: '1900:-0',
defaultDate: 0
};
}
var fieldType = scope.field.fieldType;
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
switch(scope.field.fieldType){
case 'textfield':
scope.input_type = 'text';
break;
case 'email':
scope.input_type = 'email';
scope.placeholder = 'joesmith@example.com';
break;
case 'number':
scope.input_type = 'text';
scope.validateRegex = /^-?\d+$/;
break;
default:
scope.input_type = 'url';
scope.placeholder = 'http://example.com';
break;
}
fieldType = 'textfield';
}
var template = getTemplateUrl(fieldType);
element.html(template).show();
var output = $compile(element.contents())(scope);
}
};
}]);

View file

@ -21,9 +21,9 @@
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}" <input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
name="{{field.fieldType}}{{index}}" name="{{field.fieldType}}{{index}}"
type="{{field.input_type}}" type="{{input_type}}"
ng-pattern="field.validateRegex" ng-pattern="validateRegex"
placeholder="{{field.placeholder}}" placeholder="{{placeholder}}"
ng-class="{ 'no-border': !!field.fieldValue }" ng-class="{ 'no-border': !!field.fieldValue }"
class="focusOn text-field-input" class="focusOn text-field-input"
ng-model="field.fieldValue" ng-model="field.fieldValue"

View file

@ -17,8 +17,6 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
$scope.formURL = "/#!/forms/" + $scope.myform._id; $scope.formURL = "/#!/forms/" + $scope.myform._id;
console.log($scope.myform);
$scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host + "/#!/forms/" + $scope.myform._id; $scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host + "/#!/forms/" + $scope.myform._id;
@ -106,7 +104,7 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
}; };
// Update existing Form // Update existing Form
$scope.update = $rootScope.update = function(updateImmediately, cb){ $scope.update = $rootScope.update = function(updateImmediately, diffChanges, cb){
refreshFrame(); refreshFrame();
var continueUpdate = true; var continueUpdate = true;
@ -120,7 +118,7 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$scope
if(!updateImmediately){ $rootScope.saveInProgress = true; } if(!updateImmediately){ $rootScope.saveInProgress = true; }
$scope.updatePromise = $http.put('/forms/'+$scope.myform._id, {form: $scope.myform}) $scope.updatePromise = $http.put('/forms/'+$scope.myform._id, { changes: diffChanges })
.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);

View file

@ -32,7 +32,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
$scope.$on('editFormFields Started', function(ngRepeatFinishedEvent) { $scope.$on('editFormFields Started', function(ngRepeatFinishedEvent) {
// console.log('hello');
$rootScope.finishedRender = false; $rootScope.finishedRender = false;
}); });
$scope.$on('editFormFields Finished', function(ngRepeatFinishedEvent) { $scope.$on('editFormFields Finished', function(ngRepeatFinishedEvent) {
@ -52,13 +51,11 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
return false; return false;
}; };
var debounceSave = function () { var debounceSave = function (diffChanges) {
$rootScope.saveInProgress = true;
$rootScope[$attrs.autoSaveCallback](true, $rootScope[$attrs.autoSaveCallback](true, diffChanges,
function(err){ function(err){
if(!err){ if(!err){
//console.log('\n\nForm data persisted -- setting pristine flag');
$formCtrl.$setPristine(); $formCtrl.$setPristine();
$formCtrl.$setUntouched(); $formCtrl.$setUntouched();
}else{ }else{
@ -70,12 +67,10 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
//Update/Save Form if any Form fields are Dirty and Touched //Update/Save Form if any Form fields are Dirty and Touched
$scope.$watch(function(newValue, oldValue) { $scope.$watch(function(newValue, oldValue) {
//console.log('introParagraphStartPage.$dirty: '+$scope.editForm.introParagraphStartPage.$dirty);
//console.log('introParagraphStartPage.$touched: '+$scope.editForm.introParagraphStartPage.$touched);
if($rootScope.finishedRender && $scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){ if($rootScope.finishedRender && $scope.anyDirtyAndTouched($scope.editForm) && !$rootScope.saveInProgress){
//console.log('Form saving started'); delete newValue.visible_form_fields;
debounceSave(); debounceSave(DeepDiff.diff(oldValue, newValue));
//console.log('introParagraphStartPage.$dirty AFTER: '+$scope.editForm.introParagraphStartPage.$dirty);
} }
}); });
@ -89,7 +84,6 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
oldValue.form_fields = _.removeDateFields(oldValue.form_fields); oldValue.form_fields = _.removeDateFields(oldValue.form_fields);
var changedFields = !_.isEqual(oldValue.form_fields,newValue.form_fields) || !_.isEqual(oldValue.startPage, newValue.startPage); var changedFields = !_.isEqual(oldValue.form_fields,newValue.form_fields) || !_.isEqual(oldValue.startPage, newValue.startPage);
var changedFieldMap = false;
if(oldValue.hasOwnProperty('plugins.oscarhost.settings.fieldMap')){ if(oldValue.hasOwnProperty('plugins.oscarhost.settings.fieldMap')){
changedFieldMap = !!oldValue.plugins.oscarhost.settings.fieldMap && !_.isEqual(oldValue.plugins.oscarhost.settings.fieldMap,newValue.plugins.oscarhost.settings.fieldMap); changedFieldMap = !!oldValue.plugins.oscarhost.settings.fieldMap && !_.isEqual(oldValue.plugins.oscarhost.settings.fieldMap,newValue.plugins.oscarhost.settings.fieldMap);
@ -117,7 +111,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
} }
//Save form ONLY IF rendering is finished, form_fields have been changed AND currently not save in progress //Save form ONLY IF rendering is finished, form_fields have been changed AND currently not save in progress
if( $rootScope.finishedRender && ((changedFields && !$formCtrl.$dirty) || changedFieldMap) && !$rootScope.saveInProgress) { if( $rootScope.finishedRender && (changedFields && !$formCtrl.$dirty) && !$rootScope.saveInProgress) {
if(savePromise) { if(savePromise) {
$timeout.cancel(savePromise); $timeout.cancel(savePromise);
@ -125,7 +119,12 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
} }
savePromise = $timeout(function() { savePromise = $timeout(function() {
debounceSave(); $rootScope.saveInProgress = true;
delete newValue.visible_form_fields;
delete newValue.visible_form_fields;
var _diff = DeepDiff.diff(oldValue, newValue);
debounceSave(_diff);
}); });
} }
//If we are finished rendering then form saving should be finished //If we are finished rendering then form saving should be finished

View file

@ -10,7 +10,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}, },
controller: function($scope){ controller: function($scope){
console.log($scope.myform);
var field_ids = _($scope.myform.form_fields).pluck('_id'); var field_ids = _($scope.myform.form_fields).pluck('_id');
for(var i=0; i<field_ids.length; i++){ for(var i=0; i<field_ids.length; i++){
$scope.myform.plugins.oscarhost.settings.fieldMap[field_ids[i]] = null; $scope.myform.plugins.oscarhost.settings.fieldMap[field_ids[i]] = null;
@ -109,7 +108,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
} }
} }
var newField = { var newField = {
title: fieldTitle, title: fieldTitle + ' ' + $scope.myform.form_fields.length+1,
fieldType: fieldType, fieldType: fieldType,
fieldValue: '', fieldValue: '',
required: true, required: true,
@ -126,7 +125,6 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}); });
} }
if(modifyForm){ if(modifyForm){
//Add newField to form_fields array //Add newField to form_fields array
$scope.myform.form_fields.push(newField); $scope.myform.form_fields.push(newField);

View file

@ -83,23 +83,24 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){ if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
switch(scope.field.fieldType){ switch(scope.field.fieldType){
case 'textfield': case 'textfield':
scope.field.input_type = 'text'; scope.input_type = 'text';
break; break;
case 'email': case 'email':
scope.field.input_type = 'email'; scope.input_type = 'email';
scope.field.placeholder = 'joesmith@example.com'; scope.placeholder = 'joesmith@example.com';
break; break;
case 'number': case 'number':
scope.field.input_type = 'text'; scope.input_type = 'text';
scope.field.validateRegex = /^-?\d+$/; scope.validateRegex = /^-?\d+$/;
break; break;
default: default:
scope.field.input_type = 'url'; scope.input_type = 'url';
scope.field.placeholder = 'http://example.com'; scope.placeholder = 'http://example.com';
break; break;
} }
fieldType = 'textfield'; fieldType = 'textfield';
} }
var template = getTemplateUrl(fieldType); var template = getTemplateUrl(fieldType);
element.html(template).show(); element.html(template).show();
var output = $compile(element.contents())(scope); var output = $compile(element.contents())(scope);

View file

@ -1,8 +1,8 @@
<div class="textfield field row" <div class="textfield field row"
ng-click="setActiveField(field._id, index, true)"> ng-click="setActiveField(field._id, index, true)">
<div class="col-xs-12 field-title row-fluid" ng-style="{'color': design.colors.questionColor}"> <div class="col-xs-12 field-title row-fluid" ng-style="{'color': design.colors.questionColor}">
<h3 class="col-xs-12"> <h3 class="col-xs-12">
<small class="field-number"> <small class="field-number">
{{index+1}} {{index+1}}
<i class="fa fa-angle-double-right" aria-hidden="true"></i> <i class="fa fa-angle-double-right" aria-hidden="true"></i>
</small> </small>
@ -12,54 +12,119 @@
<span class="required-error" ng-show="!field.required"> <span class="required-error" ng-show="!field.required">
({{ 'OPTIONAL' | translate }}) ({{ 'OPTIONAL' | translate }})
</span> </span>
</h3> </h3>
<p class="col-xs-12"> <p class="col-xs-12">
<small>{{field.description}}</small> <small>{{field.description}}</small>
</p> </p>
</div> </div>
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}" <input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
name="{{field.fieldType}}{{index}}" name="{{field.fieldType}}{{index}}"
type="{{field.input_type}}" type="{{input_type}}"
ng-pattern="field.validateRegex" ng-pattern="validateRegex"
placeholder="{{field.placeholder}}" placeholder="{{placeholder}}"
ng-class="{ 'no-border': !!field.fieldValue }" ng-class="{ 'no-border': !!field.fieldValue }"
class="focusOn text-field-input" class="focusOn text-field-input"
ng-model="field.fieldValue" ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
value="field.fieldValue" value="field.fieldValue"
ng-focus="setActiveField(field._id, index, true)" ng-focus="setActiveField(field._id, index, true)"
on-enter-or-tab-key="nextField()" on-enter-or-tab-key="nextField()"
on-tab-and-shift-key="prevField()" on-tab-and-shift-key="prevField()"
ng-required="field.required" ng-required="field.required"
ng-disabled="field.disabled" ng-disabled="field.disabled"
aria-describedby="inputError2Status"> aria-describedby="inputError2Status">
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert"> <div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span> <span class="sr-only">Error:</span>
<span ng-if="field.fieldType == 'email'"> {{ 'ERROR_EMAIL_INVALID' | translate }} </span> <span ng-if="field.fieldType == 'email'"> {{ 'ERROR_EMAIL_INVALID' | translate }} </span>
<span ng-if="field.validateRegex"> {{ 'ERROR_NOT_A_NUMBER' | translate }} </span> <span ng-if="field.validateRegex"> {{ 'ERROR_NOT_A_NUMBER' | translate }} </span>
<span ng-if="field.fieldType == 'link'"> {{ 'ERROR_URL_INVALID' | translate }} </span> <span ng-if="field.fieldType == 'link'"> {{ 'ERROR_URL_INVALID' | translate }} </span>
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<div class="btn btn-lg btn-default hidden-xs" <div class="btn btn-lg btn-default col-xs-12 col-sm-4 hidden-xs"
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)"> style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid" <button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}" ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
ng-click="$root.nextField()" ng-click="$root.nextField()"
class="btn col-sm-5 col-xs-5"> class="btn col-sm-5 col-xs-5">
{{ 'OK' | translate }} <i class="fa fa-check"></i> {{ 'OK' | translate }} <i class="fa fa-check"></i>
</button> </button>
<div class="col-xs-6 col-sm-3" style="margin-top:0.2em"> <div class="col-xs-6 col-sm-3" style="margin-top:0.2em">
<small style="color:#ddd; font-size:70%"> <small style="color:#ddd; font-size:70%">
{{ 'ENTER' | translate }} {{ 'ENTER' | translate }}
</small> </small>
</div> </div>
</div>
</div>
<div class="textfield field row"
ng-click="setActiveField(field._id, index, true)">
<div class="col-xs-12 field-title row-fluid" ng-style="{'color': design.colors.questionColor}">
<h3 class="col-xs-12">
<small class="field-number">
{{index+1}}
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
</small>
{{field.title}}
<span class="required-error" ng-show="!field.required">
({{ 'OPTIONAL' | translate }})
</span>
</h3>
<p class="col-xs-12">
<small>{{field.description}}</small>
</p>
</div>
<div class="col-xs-12 field-input">
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
name="{{field.fieldType}}{{index}}"
type="{{input_type}}"
ng-pattern="validateRegex"
placeholder="{{placeholder}}"
ng-class="{ 'no-border': !!field.fieldValue }"
class="focusOn text-field-input"
ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }"
value="field.fieldValue"
ng-focus="setActiveField(field._id, index, true)"
on-enter-or-tab-key="nextField()"
on-tab-and-shift-key="prevField()"
ng-required="field.required"
ng-disabled="field.disabled"
aria-describedby="inputError2Status">
</div>
<div class="col-xs-12">
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
<span ng-if="field.fieldType == 'email'"> {{ 'ERROR_EMAIL_INVALID' | translate }} </span>
<span ng-if="field.validateRegex"> {{ 'ERROR_NOT_A_NUMBER' | translate }} </span>
<span ng-if="field.fieldType == 'link'"> {{ 'ERROR_URL_INVALID' | translate }} </span>
</div>
</div>
</div>
<div>
<div class="btn btn-lg btn-default col-xs-12 col-sm-4 hidden-xs"
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
ng-click="$root.nextField()"
class="btn col-sm-5 col-xs-5">
{{ 'OK' | translate }} <i class="fa fa-check"></i>
</button>
<div class="col-xs-6 col-sm-3" style="margin-top:0.2em">
<small style="color:#ddd; font-size:70%">
{{ 'ENTER' | translate }}
</small>
</div>
</div> </div>
</div> </div>