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 diff --git a/promise_button.js b/promise_button.js index bfa925d..4b52436 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 + * + * 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 + * 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,34 @@ angular.module('ngPromiseButton', []) promisePending: '@', promiseSuccess: '@', promiseError: '@', + promiseRevert: '@', // revert attribute + promiseSuccessClass: '@', // success class attribute + promiseErrorClass: '@', // error class attribute + promiseSubmit: '=', // submit form }, 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; var intervalTick = function() { - if (scope.state != 'ceaseInterval') { + if (ceaseButton && scope.state != 'ceaseInterval') { return; } @@ -50,7 +71,7 @@ angular.module('ngPromiseButton', []) } else { ceaseTimer = $timeout(intervalTick, 1000); } - } + }; scope.onClick = function() { if (scope.state == 'progress') { @@ -63,11 +84,13 @@ 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() { if (scope.state == 'progress') { @@ -75,17 +98,62 @@ angular.module('ngPromiseButton', []) } var promise = scope.promiseButton(); - scope.state = 'progress'; + if(promise){ + + promise = promise.$promise ? promise.$promise : promise; // handle ngResource + + 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'; + + // 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); } }; - }); + }]);