From 5a9b0f80d9f42624f99a6466c944cb05466ce3e8 Mon Sep 17 00:00:00 2001 From: Chris Turnbull Date: Sat, 9 Jan 2016 16:43:38 +0000 Subject: [PATCH 1/5] Addded functionality * handle ngResource promise and null/empty promises * attribute to submit form - if a promise is supplied the button turns into type="submit" * attributes for button success/error class - defaults to btn-success/btn-danger * attribute to revert to original class after revert period * classes for span elements & use fa-spin class * added dependency injection annotations --- promise_button.js | 115 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/promise_button.js b/promise_button.js index bfa925d..3e0892f 100644 --- a/promise_button.js +++ b/promise_button.js @@ -5,18 +5,26 @@ * @name ngPromiseButton.directive:promiseButton * @description promise-button attribute expect function call. when button is pressed, function is called and button displays progress and result of the promise. * # promiseButton + * + * Addded functionality: + * handle ngResource promise and null/empty promises + * attribute to submit form - if a promise is supplied the button turns into type="submit" + * attributes for button success/error class - defaults to btn-success/btn-danger + * attribute to revert to original class after revert period + * classes for span elements & use fa-spin class + * added dependency injection annotations */ angular.module('ngPromiseButton', []) - .directive('promiseButton', function ($compile, $timeout) { + .directive('promiseButton', ['$compile', '$timeout' , function ($compile, $timeout) { return { restrict: 'A', template: [ '', - ' ', - 'Cancel in {{ceaseIntervalSec}}s', - ' {{labelPending}}', - ' {{labelSuccess}}', - ' {{labelError}}', + ' ', + 'Cancel in {{ceaseIntervalSec}}s', + ' {{labelPending}}', + ' {{labelSuccess}}', + ' {{labelError}}', '' ].join(''), transclude:true, @@ -26,21 +34,37 @@ angular.module('ngPromiseButton', []) promisePending: '@', promiseSuccess: '@', promiseError: '@', + promiseRevert: '@', // revert attribute + promiseSuccessClass: '@', // success class attribute + promiseErrorClass: '@', // error class attribute + promiseSubmit: '@', // submit form attribute }, link: function postLink(scope, element, attrs) { element.attr('ng-click', 'onClick()'); element.removeAttr('promise-button'); - element.find('[ng-transclude]').removeAttr('ng-transclude'); + element.find('[ng-transclude]').removeAttr('ng-transclude'); + // store whether to revert + var revert= scope.promiseRevert == 'none' ? false : true + scope.state = 'idle'; scope.labelPending = scope.promisePending || ''; - scope.labelSuccess = scope.promiseSuccess || 'OK!'; - scope.labelError = scope.promiseError || 'Failed!'; - + scope.labelSuccess = scope.promiseSuccess || ''; + scope.labelError = scope.promiseError || 'Failed'; + scope.revert = Number(scope.promiseRevert) || 4000; // revert attribute + scope.successClass = scope.promiseSuccessClass != null || scope.promiseSuccessClass == "none" ? scope.promiseSuccessClass : 'btn-success'; // success class attribute + scope.errorClass = scope.promiseErrorClass != null || scope.promiseErrorClass == "none" ? scope.promiseErrorClass : 'btn-danger'; // success class attribute + var ceaseTimer = null; - + + // only show cancel if there's cease-period attribute + var ceaseButton = scope.promiseCeasePeriod ? true : false; + + // change button to type="submit" if there's submit attribute + var submit= scope.promiseSubmit == 'true' ? true: false; + var intervalTick = function() { - if (scope.state != 'ceaseInterval') { + if (ceaseButton && scope.state != 'ceaseInterval') { return; } @@ -63,10 +87,12 @@ angular.module('ngPromiseButton', []) return; } - scope.state = 'ceaseInterval'; - scope.ceaseIntervalSec = Number(scope.promiseCeasePeriod) || 0; - scope.ceaseIntervalSec += 1; - intervalTick(); + if(ceaseButton){ + scope.state = 'ceaseInterval'; + } + scope.ceaseIntervalSec = Number(scope.promiseCeasePeriod) || 0; + scope.ceaseIntervalSec += 1; + intervalTick(); } scope.start = function() { @@ -75,17 +101,56 @@ angular.module('ngPromiseButton', []) } var promise = scope.promiseButton(); - scope.state = 'progress'; + if(promise){ + + promise = promise.$promise ? promise.$promise : promise; // handle ngResource + if(submit){ element.attr('type', 'submit'); } + + scope.state = 'progress'; + + // check if success/error classes are already applied + var originalClass= {}; + originalClass.success= element.hasClass(scope.successClass) ? true : false; + originalClass.error= element.hasClass(scope.errorClass) ? true : false; + + promise + .then(function() { + scope.state = 'success'; + + // remove error class & add success class + element.removeClass(scope.errorClass); + element.addClass(scope.successClass); + element[0].blur(); + + // revert to orginal class + if(revert){ + $timeout(function() { + scope.state = 'idle'; + if(originalClass.error) element.addClass(scope.errorClass); + if(!originalClass.success) element.removeClass(scope.successClass); + }, scope.revert); + } + }) + .catch(function() { + scope.state = 'error'; + + // remove success class & add error class + element.removeClass(scope.successClass); + element.addClass(scope.errorClass); + element[0].blur(); - promise - .then(function() { - scope.state = 'success'; - }) - .catch(function() { - scope.state = 'error'; - }); + // revert to orginal class + if(revert){ + $timeout(function() { + scope.state = 'idle'; + if(originalClass.success) element.addClass(scope.successClass); + if(!originalClass.error) element.removeClass(scope.errorClass); + }, scope.revert); + } + }) + } } $compile(element)(scope); } }; - }); + }]); From 927b6537ada14889d70a92cfd6be2711c5a828bd Mon Sep 17 00:00:00 2001 From: Chris Turnbull Date: Sat, 9 Jan 2016 16:45:53 +0000 Subject: [PATCH 2/5] Update promise_button.js --- promise_button.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promise_button.js b/promise_button.js index 3e0892f..6a28d3f 100644 --- a/promise_button.js +++ b/promise_button.js @@ -6,7 +6,7 @@ * @description promise-button attribute expect function call. when button is pressed, function is called and button displays progress and result of the promise. * # promiseButton * - * Addded functionality: + * Added functionality: * handle ngResource promise and null/empty promises * attribute to submit form - if a promise is supplied the button turns into type="submit" * attributes for button success/error class - defaults to btn-success/btn-danger From 1e01398e4acb0cba41e6491a8789a93aa08833fc Mon Sep 17 00:00:00 2001 From: Chris Turnbull Date: Mon, 18 Jan 2016 22:46:35 +0000 Subject: [PATCH 3/5] Update promise_button.js Added fix for bug in some browsers when submitting form --- promise_button.js | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/promise_button.js b/promise_button.js index 6a28d3f..1b49c59 100644 --- a/promise_button.js +++ b/promise_button.js @@ -37,7 +37,7 @@ angular.module('ngPromiseButton', []) promiseRevert: '@', // revert attribute promiseSuccessClass: '@', // success class attribute promiseErrorClass: '@', // error class attribute - promiseSubmit: '@', // submit form attribute + promiseSubmit: '=', // submit form }, link: function postLink(scope, element, attrs) { element.attr('ng-click', 'onClick()'); @@ -45,24 +45,21 @@ angular.module('ngPromiseButton', []) element.find('[ng-transclude]').removeAttr('ng-transclude'); // store whether to revert - var revert= scope.promiseRevert == 'none' ? false : true + var revert= scope.promiseRevert == 'none' ? false : true; scope.state = 'idle'; scope.labelPending = scope.promisePending || ''; scope.labelSuccess = scope.promiseSuccess || ''; scope.labelError = scope.promiseError || 'Failed'; scope.revert = Number(scope.promiseRevert) || 4000; // revert attribute - scope.successClass = scope.promiseSuccessClass != null || scope.promiseSuccessClass == "none" ? scope.promiseSuccessClass : 'btn-success'; // success class attribute - scope.errorClass = scope.promiseErrorClass != null || scope.promiseErrorClass == "none" ? scope.promiseErrorClass : 'btn-danger'; // success class attribute + scope.successClass = scope.promiseSuccessClass !== null || scope.promiseSuccessClass == "none" ? scope.promiseSuccessClass : 'btn-success'; // success class attribute + scope.errorClass = scope.promiseErrorClass !== null || scope.promiseErrorClass == "none" ? scope.promiseErrorClass : 'btn-danger'; // success class attribute var ceaseTimer = null; - // only show cancel if there's cease-period attribute - var ceaseButton = scope.promiseCeasePeriod ? true : false; - - // change button to type="submit" if there's submit attribute - var submit= scope.promiseSubmit == 'true' ? true: false; - + // only show cancel if there's cease-period attribute + var ceaseButton = scope.promiseCeasePeriod ? true : false; + var intervalTick = function() { if (ceaseButton && scope.state != 'ceaseInterval') { return; @@ -74,7 +71,7 @@ angular.module('ngPromiseButton', []) } else { ceaseTimer = $timeout(intervalTick, 1000); } - } + }; scope.onClick = function() { if (scope.state == 'progress') { @@ -87,13 +84,13 @@ angular.module('ngPromiseButton', []) return; } - if(ceaseButton){ - scope.state = 'ceaseInterval'; - } - scope.ceaseIntervalSec = Number(scope.promiseCeasePeriod) || 0; - scope.ceaseIntervalSec += 1; - intervalTick(); - } + if(ceaseButton){ + scope.state = 'ceaseInterval'; + } + scope.ceaseIntervalSec = Number(scope.promiseCeasePeriod) || 0; + scope.ceaseIntervalSec += 1; + intervalTick(); + }; scope.start = function() { if (scope.state == 'progress') { @@ -104,7 +101,13 @@ angular.module('ngPromiseButton', []) if(promise){ promise = promise.$promise ? promise.$promise : promise; // handle ngResource - if(submit){ element.attr('type', 'submit'); } + + if(scope.promiseSubmit){ + var formEl= element[0].form; + element.attr('type', 'submit'); // change button to type "submit" + angular.element(formEl).triggerHandler('submit'); // trigger submit event + scope.promiseSubmit.$submitted= true ; // update submitted + } scope.state = 'progress'; @@ -147,9 +150,9 @@ angular.module('ngPromiseButton', []) if(!originalClass.error) element.removeClass(scope.errorClass); }, scope.revert); } - }) + }); } - } + }; $compile(element)(scope); } }; From 51a8215d5a059926edb190757e13e62b9ad8bfc1 Mon Sep 17 00:00:00 2001 From: Chris Turnbull Date: Wed, 20 Jan 2016 21:06:48 +0000 Subject: [PATCH 4/5] Update promise_button.js --- promise_button.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/promise_button.js b/promise_button.js index 1b49c59..4b52436 100644 --- a/promise_button.js +++ b/promise_button.js @@ -52,8 +52,8 @@ angular.module('ngPromiseButton', []) scope.labelSuccess = scope.promiseSuccess || ''; scope.labelError = scope.promiseError || 'Failed'; scope.revert = Number(scope.promiseRevert) || 4000; // revert attribute - scope.successClass = scope.promiseSuccessClass !== null || scope.promiseSuccessClass == "none" ? scope.promiseSuccessClass : 'btn-success'; // success class attribute - scope.errorClass = scope.promiseErrorClass !== null || scope.promiseErrorClass == "none" ? scope.promiseErrorClass : 'btn-danger'; // success class attribute + scope.successClass = scope.promiseSuccessClass != null || scope.promiseSuccessClass == "none" ? scope.promiseSuccessClass : 'btn-success'; // success class attribute + scope.errorClass = scope.promiseErrorClass != null || scope.promiseErrorClass == "none" ? scope.promiseErrorClass : 'btn-danger'; // success class attribute var ceaseTimer = null; From 4b15c21fba7523e4e4001e4f40b855288ff59f8f Mon Sep 17 00:00:00 2001 From: Chris Turnbull Date: Wed, 20 Jan 2016 21:19:27 +0000 Subject: [PATCH 5/5] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a4edb05..604cbef 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ Simple angular directive for button showing progress and result of promise (eg resource request call) +This is a fork with added functionality. + +FORK DEMO: http://embed.plnkr.co/WRaLC2/ + + ## What's this? DEMO: http://plnkr.co/edit/pHwqib?p=preview