Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functionality #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
122 changes: 95 additions & 27 deletions promise_button.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like messing the code with build related overhead like this. This can be done build-time using for example https://github.com/olov/ng-annotate

return {
restrict: 'A',
template: [
'<span>',
'<span ng-show="state != \'ceaseInterval\'" ng-transclude></span>&nbsp;',
'<span ng-show="state == \'ceaseInterval\'">Cancel in {{ceaseIntervalSec}}s</span>',
'<span ng-show="state == \'progress\'"><span class="spinner fa fa-refresh"></span> {{labelPending}}</span>',
'<span ng-show="state == \'success\'"><span class="fa fa-check"></span> {{labelSuccess}}</span>',
'<span ng-show="state == \'error\'"><span class="fa fa-times"></span> {{labelError}}</span>',
'<span class="promise-button-idle" ng-show="state != \'ceaseInterval\'" ng-transclude></span>&nbsp;',
'<span class="promise-button-countdown" ng-show="state == \'ceaseInterval\'">Cancel in {{ceaseIntervalSec}}s</span>',
'<span class="promise-button-progress" ng-show="state == \'progress\'"><span class="fa fa-refresh fa-spin"></span> {{labelPending}}</span>',
'<span class="promise-button-success" ng-show="state == \'success\'"><span class="fa fa-check"></span> {{labelSuccess}}</span>',
'<span class="promise-button-error" ng-show="state == \'error\'"><span class="fa fa-times"></span> {{labelError}}</span>',
'</span>'
].join(''),
transclude:true,
Expand All @@ -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;
}

Expand All @@ -50,7 +71,7 @@ angular.module('ngPromiseButton', [])
} else {
ceaseTimer = $timeout(intervalTick, 1000);
}
}
};

scope.onClick = function() {
if (scope.state == 'progress') {
Expand All @@ -63,29 +84,76 @@ 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') {
return;
}

var promise = scope.promiseButton();
scope.state = 'progress';
if(promise){

promise = promise.$promise ? promise.$promise : promise; // handle ngResource

if(scope.promiseSubmit){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not getting purpose of this. What is the difference from just setting button as submit type in the parent scope?

<form>
...
<button type='submit' promise-button='submit()'>Save</button>
</form>

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);
}
};
});
}]);