diff --git a/bower.json b/bower.json index ba142d1..b5f602b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ng-promise-status", - "version": "0.1.1", + "version": "0.1.2", "authors": [ "Barak Chamo " ], diff --git a/demo/demo.css b/demo/demo.css index 0cb3cae..9fd1433 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -17,6 +17,7 @@ button { will-change: all; } + /*Default status-button usage demo styles*/ button:not(.btn){ @@ -63,4 +64,12 @@ button.inprogress { background: #FFF9C4; border: 1px solid #FFF59D; border-radius: 1em; +} + + +/*Default status-bar usage demo styles*/ + +/*Hide idle alerts*/ +.alert.idle { + display: none; } \ No newline at end of file diff --git a/demo/demo.html b/demo/demo.html index 25057af..ebea0e8 100644 --- a/demo/demo.html +++ b/demo/demo.html @@ -21,6 +21,13 @@

ngPromiseStatus promise-aware UI elements.


A collection of Angular.js promise-aware directives and UI elements for your project

by Barak Chamo

+
+ + +   + +
+
@@ -33,9 +40,10 @@

by Barak Chamo

Examples

- + +

Simple usage

-

This is a simplest use of the directive with only a promise (or array of) passed (and some default styles).

+

This is a simplest use of the directive with only a promise (or array of) passed (and some default styles). See valid values for promises


@@ -109,7 +117,71 @@

Promise Values

+ +

+ + + + +

Example with custom classes and promise values

+

status-bar makes it really easy to create an alert container that reacts to promises.

+

This is an example with Bootstrap alert styles and the promise value exposed. It works just like the button, pass a promise or array of promises. Click on the buttons above to show the promises.

+
+
+
+
+ + Success {{$value}} + + +

I'm here, trigger the success button above.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+ +
+ + Error {{$value}} + + +

Me too, trigger the error button above.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+ +
+ + Value {{$value}} + + +

This one is for a value promise, try the button in the first row.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+
+


@@ -129,10 +201,89 @@
var app = angular.module('myApp', ['ngPromiseStatus']);

and you're good to go!

+
+ + + + +

Multiple Promises (and other valid inputs)

+

The directives are built to work with multiple type of inputs:

+
+
promises
+
The promise from a $q.defer() or similar interfaces.
+
deffers
+
The deffered itself, saves the need of saving multiple references on scope.
+
promise arrays
+
An array of promises or deffereds.
+
other values
+
Any other value will not break but rather resolve immediately (implements $q.when).
+
+ + +
+ + +

CSS Classes

+

By default, the directives apply 4 classes to the elements depending on the state of the promise:

+
+
idle
+
There is no current promise in progress.
+
+
inprogress
+
There promise is still in progress, inprogress is used instead of progress so that it doesn't clash with Bootstrap progress bars.
+
+
success
+
The promise was resolved.
+
+
error
+
The promise was rejected.
+
+ +

ngPromiseStatus intentionally doesn't rely on any additional CSS files or required styling. It provides an easy interface in the form of class management that allows you to easy interact with promises using your existing styles and with no additional bloat to your project.

+ +
+ +

Scope Properties

+

The directive exposes special scope properties to the button's content that allow you to customize the button's content and interact with the promise's values and state.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nametypedetails
$statusstringThe status of the promise, same as the default class names: idle, inprogress, success and error
$valueanyWhatever value is passed upon resolution or rejection of the promise. If an array of promises is used, $value will be an ordered array of said whatevers.
$donebooleanA convenience boolean that indicates if a promise is in progress.
$classstringThe class that is currently applied, overwritten with your custom configuration if provided.
+ +
+ - -

Basic Use

-

The statusButton directive can be used by simply using the directive as a tag or attribute and passing a promise (or array of).

+ + +

Basic Use See default classes

+

The statusButton directive can be used by simply using the directive as a tag or attribute and passing a promise (or array of). See valid values for promises

When the button is clicked an internal handler is fired and assigns a promise handler to the assigned promise. Note that for that reason new promises are only detected on click.

Any ng-click handler assigned to the element will be called before the internal handler so you can easily use the same 'click' event to both assign a promise in your controller and for it to be detected by the button.

@@ -182,44 +333,6 @@

Basic Use

- -
- - -

Multiple Promises (and other valid inputs)

-

The directive is built to work with multiple type of inputs:

-
-
promises
-
The promise from a $q.defer().
-
deffers
-
The deffered itself, saves the need of saving multiple references on scope.
-
promise arrays
-
An array of promises or deffereds.
-
other values
-
Any other value will not break but rather resolve immediately (implements $q.when).
-
- - -
- - -

CSS Classes

-

By default, the directive applies 4 classes to the button depending on the state of the promise:

-
-
idle
-
There is no current promise in progress.
-
-
inprogress
-
There promise is still in progress, inprogress is used instead of progress so that it doesn't clash with Bootstrap progress bars.
-
-
success
-
The promise was resolved.
-
-
error
-
The promise was rejected.
-
- -

ngPromiseStatus intentionally doesn't rely on any additional CSS files or required styling. It provides an easy interface in the form of class management that allows you to easy interact with promises using your existing styles and with no additional bloat to your project.


@@ -334,74 +447,179 @@

Custom Configuration


-

Scope Properties

-

The directive exposes special scope properties to the button's content that allow you to customize the button's content and interact with the promise's values and state.

+

Scope Properties Available scope properties

+ + +

Here's an example of using scope properties inside the button (promise is rejected with 'Error!'):

+
+
+
+

Result:

+
+ + What's up? {{$value}} + +
+
+
+

Template:

+
+<status-button 
+  ng-click="setButtonPromise()" 
+  promise="buttonPromise" 
+  options="bootstrapConfig">
+  <span>Success {{$value}}</span>
+</status-button>
+                  
+
+
+
+ + + + + +
+ + +

Just like the statusButton, the statusBar directive can be easily put to work as a tag or attribute and passing a promise (or array of). See valid values for promises

+

When a promise is set and re-set a promise handler is assigned. This is managed by watching the promise passed but other hooks will soon be implemented.

+ +
+ + +

Custom Configuration

+

You can configure some parameters of statusBar - classes applied on promise stages, delay and watch:

+ - + - + + - - - + + + + - - - + + + + - + - + + + + + + + + + + + + + +
name typedefault details
$statusidle_class stringThe status of the promise, same as the default class names: idle, inprogress, success and erroridleThe initial class applied to the bar and will applied after completion if a delay is specified.
$valueanyWhatever value is passed upon resolution or rejection of the promise. If an array of promises is used, $value will be an ordered array of said whatevers.progress_classstringinprogressThe class that will be applied to the bar whenever a promise is in progress.
$donebooleanA convenience boolean that indicates if a promise is in progress.success_classstringsuccessThe class that will be applied to the bar when a promise is resolved (completed successfully).
$classerror_class stringThe class that is currently applied, overwritten with your custom configuration if provided.errorThe class that will be applied to the bar when a promise is rejected (resulted in an error).
watchbooleantrueA toggle that determines if the promise is watched. This should be left true for now until additional hooks are implemented.
delaynumber0The time (in milliseconds) it takes for the bar to return to idle state upon promise completion (either success or error). 0 will not return to idle.
- -

Here's an example of using scope properties inside the button (promise is rejected with 'Error!'):

+

Usage with Bootstrap classes and conditional content.

+ +

The statusBar directive assigns status classes to a container, it works well with Bootstrap <alert>s and other CSS framework components.

+ +
+ + +

Here's an example with custom configuration and conditional content based on exposed scope properties.

Result:

- - What's up? {{$value}} + + Give it a go! + +

+ + +

The bar will show up here

+
+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + +
-

Template:

+

Bar Template:

+<status-bar class="alert"
+  promise="examplePromise" 
+  options="alertConfig">
+  <span ng-switch="$status">
+    <strong ng-switch-when="inprogress">
+      Wait for it...
+    </strong> 
+    <strong ng-switch-when="success">
+      Woohoo!
+    </strong> 
+    <strong ng-switch-when="error">
+      Bummer...
+    </strong> 
+
+    {{$value}}
+  </span> 
+</status-bar>
+                  
+ +

Button Template:

+
 <status-button 
-  ng-click="setButtonPromise()" 
-  promise="buttonPromise" 
-  options="bootstrapConfig">
-  <span>Success {{$value}}</span>
+  ng-click="setExamplePromise()" 
+  promise="examplePromise">
+  <span>Give it a go!</span> 
 </status-button>
                   
- - - - + + + @@ -410,5 +628,9 @@

Scope Properties

prettyPrint(); })(); + + + + \ No newline at end of file diff --git a/demo/demo.js b/demo/demo.js index de0a8bd..2a6f108 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -26,6 +26,14 @@ app.controller('demoController', ['$scope', '$q', '$timeout', function($scope, $ delay: 2000 }; + $scope.alertConfig = { + success_class: 'alert-success', + progress_class: 'alert-warning', + error_class: 'alert-danger' + }; + + // Button demo methods + // Create a new promise that will succeed in 3 seconds $scope.setSuccessPromise = function(){ // Reassign $scope.promise with a new promise @@ -42,7 +50,7 @@ app.controller('demoController', ['$scope', '$q', '$timeout', function($scope, $ }; // Create a new promise that will succeed in 3 seconds - $scope.setValuePromise = function(){ + $scope.setalertValuePromise = function(){ // Reassign $scope.promise with a new promise $scope.valuePromise = {value: 'this ain\'t a promise!'}; }; @@ -61,4 +69,58 @@ app.controller('demoController', ['$scope', '$q', '$timeout', function($scope, $ $scope.errorPromise.reject('Error!'); }, 3000); }; + + + // Alert demo methods + // Create a new promise that will succeed in 3 seconds + $scope.setAlertSuccessPromise = function(){ + // Reassign $scope.promise with a new promise + $scope.alertSuccessPromise = $q.defer(); + + $scope.alertSuccessPromise.promise.then(function(){ + console.log('Done from controller'); + }); + + // Set a timeout for 3 seconds and resolve the promise + $timeout(function(){ + $scope.alertSuccessPromise.resolve('Done!'); + }, 3000); + }; + + // Alert demo methods + // Create a new promise that will succeed in 3 seconds + $scope.setExamplePromise = function(){ + // Reassign $scope.promise with a new promise + $scope.examplePromise = $q.defer(); + + $scope.examplePromise.promise.then(function(){ + console.log('Done from controller'); + }); + + // Set a timeout for 3 seconds and resolve the promise + $timeout(function(){ + $scope.examplePromise.resolve('Done!'); + }, 3000); + }; + + // Create a new promise that will succeed in 3 seconds + $scope.setAlertValuePromise = function(){ + // Reassign $scope.promise with a new promise + $scope.alertValuePromise = {value: 'this ain\'t a promise!'}; + }; + + // Create a new promise that will fail within 3 seconds + $scope.setAlertErrorPromise = function(){ + // Reassign $scope.promise with a new promise + $scope.alertErrorPromise = $q.defer(); + + $scope.alertErrorPromise.promise.then(function(){ + console.log('Error from controller'); + }); + + // Set a timeout for 3 seconds and resolve the promise + $timeout(function(){ + $scope.alertErrorPromise.reject('Error!'); + }, 3000); + }; }]); \ No newline at end of file diff --git a/dist/directive.css b/dist/directive.css index 7365cab..f02dabc 100644 --- a/dist/directive.css +++ b/dist/directive.css @@ -1,7 +1,7 @@ /*! * ng-promise-status * - * Version: 0.0.1 - 2015-04-28T15:06:53.753Z + * Version: 0.1.1 - 2015-05-06T15:36:52.550Z * License: MIT */ diff --git a/dist/directive.js b/dist/directive.js index 9c170d8..5cbdfe7 100644 --- a/dist/directive.js +++ b/dist/directive.js @@ -1,7 +1,7 @@ /*! * ng-promise-status * - * Version: 0.0.1 - 2015-04-28T16:20:08.907Z + * Version: 0.1.1 - 2015-05-07T10:40:47.345Z * License: MIT */ @@ -106,6 +106,10 @@ angular.module('ngPromiseStatus', []) } + /* + Render + */ + // Transclude the button's content //- Transclusion is done here and not with ngTransclude //- because we want to expose some scope goodies back to @@ -115,5 +119,137 @@ angular.module('ngPromiseStatus', []) }); } }; -}]); +}]) + +.directive('statusBar', ['$timeout', '$q', function ( $timeout, $q ) { + return { + restrict: 'AE', + transclude: true, + replace: true, + template: '
', + scope: { + promise: '=', + options: '=' + }, + link: function (scope, element, attrs, controller, transclude) { + /* + Scope properties + */ + + // Set directive configuration defaults + scope.$config = { + // Status classes + success_class: 'success', + error_class: 'error', + progress_class: 'inprogress', + idle_class: 'idle', + + // Timeout for clearing status (back to idle) - 0 is disabled, keeps the status + delay: 0, + + // Watch the promise property for re-assignments + watch: true + }; + + // Apply custom configuration + angular.extend(scope.$config, scope.options); + + // Initialize exposed scope properties + scope.$status = 'idle'; + scope.$class = scope.$config.idle_class; + + + /* + Private Methods + */ + + function setPromise(promise){ + var p; + + // Reset exposed scope values + setProps('inprogress', scope.$config.progress_class, false, undefined); + + // Check for supported types + p = promise.propertyIsEnumerable('promise') ? promise.promise : promise; + p = p instanceof Array ? p : [p]; + + $q.all(p) + .then(success, error, progress); + } + + // Handle sucessful promises + function success(value){ + setProps('success', scope.$config.success_class, true, scope.promise instanceof Array ? value : value[0]); + } + + // Handle promise errors + function error(err){ + setProps('error', scope.$config.error_class, true, err); + } + + // Handle promise notify (progress) + function progress(value){ + scope.$value = value; + } + + // Update exposed scope properties + function setProps(status, className, done, value){ + scope.$status = status; + scope.$class = className; + scope.$done = done; + scope.$value = value; + + // If done and delay is set, set a timeout to clear the class + if (scope.$config.delay > 0 && done) { + $timeout(function(){ + scope.$status = 'idle'; + scope.$class = scope.$config.idle_class; + }, scope.$config.delay); + } + } + + + /* + Event handling + */ + + // I DON'T LIKE THIS! + if (scope.$config.watch) { + var watch = scope.$watch('promise', function(promise){ + if (!promise) return; + + $timeout(function(){ + setPromise(promise); + }); + }); + + // Scope cleanup + scope.$on('$destroy', function(){ + // Clear the watch + watch(); + }); + } + + scope.$on('promise:status', function(){ + $timeout(function(){ + setPromise(scope.promise); + }); + }); + + + /* + Render + */ + + // Transclude the bar's content + //- Transclusion is done here and not with ngTransclude + //- because we want to expose some scope goodies back to + //- the original template from the directive's isolate scope. + transclude(scope, function(clone){ + element.append(clone); + }); + } + }; +}]) +; angular.module("ngPromiseStatus").run(["$templateCache", function($templateCache) {$templateCache.put("directive.html","");}]); \ No newline at end of file diff --git a/dist/directive.min.css b/dist/directive.min.css index 2afda32..db10295 100644 --- a/dist/directive.min.css +++ b/dist/directive.min.css @@ -1,6 +1,6 @@ /*! * ng-promise-status * - * Version: 0.0.1 - 2015-04-28T15:06:53.753Z + * Version: 0.1.1 - 2015-05-06T15:36:52.550Z * License: MIT */.the-directive{color:green}.the-directive button{padding:10px;color:#00f} \ No newline at end of file diff --git a/dist/directive.min.js b/dist/directive.min.js index a8ce128..cbcf357 100644 --- a/dist/directive.min.js +++ b/dist/directive.min.js @@ -1,7 +1,7 @@ /*! * ng-promise-status * - * Version: 0.0.1 - 2015-04-28T16:20:08.907Z + * Version: 0.1.1 - 2015-05-07T10:40:47.345Z * License: MIT */ -"use strict";angular.module("ngPromiseStatus",[]).directive("statusButton",["$timeout","$q",function(s,t){return{restrict:"AE",templateUrl:"directive.html",replace:!0,transclude:!0,scope:{promise:"=",options:"="},link:function(e,n,c,i,o){function r(s){var n;g("inprogress",e.$config.progress_class,!1,void 0),n=s.propertyIsEnumerable("promise")?s.promise:s,n=n instanceof Array?n:[n],t.all(n).then(a,l,u)}function a(s){g("success",e.$config.success_class,!0,e.promise instanceof Array?s:s[0])}function l(s){g("error",e.$config.error_class,!0,s)}function u(s){e.$value=s}function g(t,n,c,i){e.$status=t,e.$class=n,e.$done=c,e.$value=i,e.$config.delay>0&&c&&s(function(){e.$status="idle",e.$class=e.$config.idle_class},e.$config.delay)}e.$config={success_class:"success",error_class:"error",progress_class:"inprogress",idle_class:"idle",progress_disable:!0,delay:0},angular.extend(e.$config,e.options),e.$status="idle",e.$class=e.$config.idle_class,e.statusButtonClick=function(t){s(function(){r(e.promise)})},o(e,function(s){n.append(s)})}}}]),angular.module("ngPromiseStatus").run(["$templateCache",function(s){s.put("directive.html",'')}]); \ No newline at end of file +"use strict";angular.module("ngPromiseStatus",[]).directive("statusButton",["$timeout","$q",function(s,n){return{restrict:"AE",templateUrl:"directive.html",replace:!0,transclude:!0,scope:{promise:"=",options:"="},link:function(e,c,t,o,i){function r(s){var c;f("inprogress",e.$config.progress_class,!1,void 0),c=s.propertyIsEnumerable("promise")?s.promise:s,c=c instanceof Array?c:[c],n.all(c).then(a,l,u)}function a(s){f("success",e.$config.success_class,!0,e.promise instanceof Array?s:s[0])}function l(s){f("error",e.$config.error_class,!0,s)}function u(s){e.$value=s}function f(n,c,t,o){e.$status=n,e.$class=c,e.$done=t,e.$value=o,e.$config.delay>0&&t&&s(function(){e.$status="idle",e.$class=e.$config.idle_class},e.$config.delay)}e.$config={success_class:"success",error_class:"error",progress_class:"inprogress",idle_class:"idle",progress_disable:!0,delay:0},angular.extend(e.$config,e.options),e.$status="idle",e.$class=e.$config.idle_class,e.statusButtonClick=function(n){s(function(){r(e.promise)})},i(e,function(s){c.append(s)})}}}]).directive("statusBar",["$timeout","$q",function(s,n){return{restrict:"AE",transclude:!0,replace:!0,template:'
',scope:{promise:"=",options:"="},link:function(e,c,t,o,i){function r(s){var c;f("inprogress",e.$config.progress_class,!1,void 0),c=s.propertyIsEnumerable("promise")?s.promise:s,c=c instanceof Array?c:[c],n.all(c).then(a,l,u)}function a(s){f("success",e.$config.success_class,!0,e.promise instanceof Array?s:s[0])}function l(s){f("error",e.$config.error_class,!0,s)}function u(s){e.$value=s}function f(n,c,t,o){e.$status=n,e.$class=c,e.$done=t,e.$value=o,e.$config.delay>0&&t&&s(function(){e.$status="idle",e.$class=e.$config.idle_class},e.$config.delay)}if(e.$config={success_class:"success",error_class:"error",progress_class:"inprogress",idle_class:"idle",delay:0,watch:!0},angular.extend(e.$config,e.options),e.$status="idle",e.$class=e.$config.idle_class,e.$config.watch){var $=e.$watch("promise",function(n){n&&s(function(){r(n)})});e.$on("$destroy",function(){$()})}e.$on("promise:status",function(){s(function(){r(e.promise)})}),i(e,function(s){c.append(s)})}}}]),angular.module("ngPromiseStatus").run(["$templateCache",function(s){s.put("directive.html",'')}]); \ No newline at end of file diff --git a/index.html b/index.html index 0110e3a..9022ba1 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,13 @@

ngPromiseStatus promise-aware UI elements.


A collection of Angular.js promise-aware directives and UI elements for your project

by Barak Chamo

+
+ + +   + +
+
@@ -33,9 +40,10 @@

by Barak Chamo

Examples

- + +

Simple usage

-

This is a simplest use of the directive with only a promise (or array of) passed (and some default styles).

+

This is a simplest use of the directive with only a promise (or array of) passed (and some default styles). See valid values for promises


@@ -109,7 +117,71 @@

Promise Values

+ +

+ + + + +

Example with custom classes and promise values

+

status-bar makes it really easy to create an alert container that reacts to promises.

+

This is an example with Bootstrap alert styles and the promise value exposed. It works just like the button, pass a promise or array of promises. Click on the buttons above to show the promises.

+
+
+
+
+ + Success {{$value}} + + +

I'm here, trigger the success button above.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+ +
+ + Error {{$value}} + + +

Me too, trigger the error button above.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+ +
+ + Value {{$value}} + + +

This one is for a value promise, try the button in the first row.

+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + + +
+
+


@@ -129,10 +201,89 @@
var app = angular.module('myApp', ['ngPromiseStatus']);

and you're good to go!

+
+ + + + +

Multiple Promises (and other valid inputs)

+

The directives are built to work with multiple type of inputs:

+
+
promises
+
The promise from a $q.defer() or similar interfaces.
+
deffers
+
The deffered itself, saves the need of saving multiple references on scope.
+
promise arrays
+
An array of promises or deffereds.
+
other values
+
Any other value will not break but rather resolve immediately (implements $q.when).
+
+ + +
+ + +

CSS Classes

+

By default, the directives apply 4 classes to the elements depending on the state of the promise:

+
+
idle
+
There is no current promise in progress.
+
+
inprogress
+
There promise is still in progress, inprogress is used instead of progress so that it doesn't clash with Bootstrap progress bars.
+
+
success
+
The promise was resolved.
+
+
error
+
The promise was rejected.
+
+ +

ngPromiseStatus intentionally doesn't rely on any additional CSS files or required styling. It provides an easy interface in the form of class management that allows you to easy interact with promises using your existing styles and with no additional bloat to your project.

+ +
+ +

Scope Properties

+

The directive exposes special scope properties to the button's content that allow you to customize the button's content and interact with the promise's values and state.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nametypedetails
$statusstringThe status of the promise, same as the default class names: idle, inprogress, success and error
$valueanyWhatever value is passed upon resolution or rejection of the promise. If an array of promises is used, $value will be an ordered array of said whatevers.
$donebooleanA convenience boolean that indicates if a promise is in progress.
$classstringThe class that is currently applied, overwritten with your custom configuration if provided.
+ +
+ - -

Basic Use

-

The statusButton directive can be used by simply using the directive as a tag or attribute and passing a promise (or array of).

+ + +

Basic Use See default classes

+

The statusButton directive can be used by simply using the directive as a tag or attribute and passing a promise (or array of). See valid values for promises

When the button is clicked an internal handler is fired and assigns a promise handler to the assigned promise. Note that for that reason new promises are only detected on click.

Any ng-click handler assigned to the element will be called before the internal handler so you can easily use the same 'click' event to both assign a promise in your controller and for it to be detected by the button.

@@ -182,44 +333,6 @@

Basic Use

- -
- - -

Multiple Promises (and other valid inputs)

-

The directive is built to work with multiple type of inputs:

-
-
promises
-
The promise from a $q.defer().
-
deffers
-
The deffered itself, saves the need of saving multiple references on scope.
-
promise arrays
-
An array of promises or deffereds.
-
other values
-
Any other value will not break but rather resolve immediately (implements $q.when).
-
- - -
- - -

CSS Classes

-

By default, the directive applies 4 classes to the button depending on the state of the promise:

-
-
idle
-
There is no current promise in progress.
-
-
inprogress
-
There promise is still in progress, inprogress is used instead of progress so that it doesn't clash with Bootstrap progress bars.
-
-
success
-
The promise was resolved.
-
-
error
-
The promise was rejected.
-
- -

ngPromiseStatus intentionally doesn't rely on any additional CSS files or required styling. It provides an easy interface in the form of class management that allows you to easy interact with promises using your existing styles and with no additional bloat to your project.


@@ -334,74 +447,179 @@

Custom Configuration


-

Scope Properties

-

The directive exposes special scope properties to the button's content that allow you to customize the button's content and interact with the promise's values and state.

+

Scope Properties Available scope properties

+ + +

Here's an example of using scope properties inside the button (promise is rejected with 'Error!'):

+
+
+
+

Result:

+
+ + What's up? {{$value}} + +
+
+
+

Template:

+
+<status-button 
+  ng-click="setButtonPromise()" 
+  promise="buttonPromise" 
+  options="bootstrapConfig">
+  <span>Success {{$value}}</span>
+</status-button>
+                  
+
+
+
+ + + + + +
+ + +

Just like the statusButton, the statusBar directive can be easily put to work as a tag or attribute and passing a promise (or array of). See valid values for promises

+

When a promise is set and re-set a promise handler is assigned. This is managed by watching the promise passed but other hooks will soon be implemented.

+ +
+ + +

Custom Configuration

+

You can configure some parameters of statusBar - classes applied on promise stages, delay and watch:

+ - + - + + - - - + + + + - - - + + + + - + - + + + + + + + + + + + + + +
name typedefault details
$statusidle_class stringThe status of the promise, same as the default class names: idle, inprogress, success and erroridleThe initial class applied to the bar and will applied after completion if a delay is specified.
$valueanyWhatever value is passed upon resolution or rejection of the promise. If an array of promises is used, $value will be an ordered array of said whatevers.progress_classstringinprogressThe class that will be applied to the bar whenever a promise is in progress.
$donebooleanA convenience boolean that indicates if a promise is in progress.success_classstringsuccessThe class that will be applied to the bar when a promise is resolved (completed successfully).
$classerror_class stringThe class that is currently applied, overwritten with your custom configuration if provided.errorThe class that will be applied to the bar when a promise is rejected (resulted in an error).
watchbooleantrueA toggle that determines if the promise is watched. This should be left true for now until additional hooks are implemented.
delaynumber0The time (in milliseconds) it takes for the bar to return to idle state upon promise completion (either success or error). 0 will not return to idle.
- -

Here's an example of using scope properties inside the button (promise is rejected with 'Error!'):

+

Usage with Bootstrap classes and conditional content.

+ +

The statusBar directive assigns status classes to a container, it works well with Bootstrap <alert>s and other CSS framework components.

+ +
+ + +

Here's an example with custom configuration and conditional content based on exposed scope properties.

Result:

- - What's up? {{$value}} + + Give it a go! + +

+ + +

The bar will show up here

+
+ + + + Wait for it... + Woohoo! + Bummer... + {{$value}} + +
-

Template:

+

Bar Template:

+<status-bar class="alert"
+  promise="examplePromise" 
+  options="alertConfig">
+  <span ng-switch="$status">
+    <strong ng-switch-when="inprogress">
+      Wait for it...
+    </strong> 
+    <strong ng-switch-when="success">
+      Woohoo!
+    </strong> 
+    <strong ng-switch-when="error">
+      Bummer...
+    </strong> 
+    
+    {{$value}}
+  </span> 
+</status-bar>
+                  
+ +

Button Template:

+
 <status-button 
-  ng-click="setButtonPromise()" 
-  promise="buttonPromise" 
-  options="bootstrapConfig">
-  <span>Success {{$value}}</span>
+  ng-click="setExamplePromise()" 
+  promise="examplePromise">
+  <span>Give it a go!</span> 
 </status-button>
                   
- - - - + + + @@ -410,5 +628,8 @@

Scope Properties

prettyPrint(); })(); + + + \ No newline at end of file diff --git a/package.json b/package.json index 475dab6..bd7c653 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ng-promise-status", - "version": "0.1.1", + "version": "0.1.2", "description": "A collection of promise-aware Angular.js directives and UI elements", "repository": "git@github.com:BarakChamo/ng-promise-status.git", "main": "dist/directive.js", diff --git a/src/directive.html b/src/directive.html index e5b97c0..987753e 100644 --- a/src/directive.html +++ b/src/directive.html @@ -1,3 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/directive.js b/src/directive.js index 158bf4a..9c930c0 100644 --- a/src/directive.js +++ b/src/directive.js @@ -98,6 +98,10 @@ angular.module('ngPromiseStatus', []) } + /* + Render + */ + // Transclude the button's content //- Transclusion is done here and not with ngTransclude //- because we want to expose some scope goodies back to @@ -107,4 +111,136 @@ angular.module('ngPromiseStatus', []) }); } }; -}]); \ No newline at end of file +}]) + +.directive('statusBar', ['$timeout', '$q', function ( $timeout, $q ) { + return { + restrict: 'AE', + transclude: true, + replace: true, + template: '
', + scope: { + promise: '=', + options: '=' + }, + link: function (scope, element, attrs, controller, transclude) { + /* + Scope properties + */ + + // Set directive configuration defaults + scope.$config = { + // Status classes + success_class: 'success', + error_class: 'error', + progress_class: 'inprogress', + idle_class: 'idle', + + // Timeout for clearing status (back to idle) - 0 is disabled, keeps the status + delay: 0, + + // Watch the promise property for re-assignments + watch: true + }; + + // Apply custom configuration + angular.extend(scope.$config, scope.options); + + // Initialize exposed scope properties + scope.$status = 'idle'; + scope.$class = scope.$config.idle_class; + + + /* + Private Methods + */ + + function setPromise(promise){ + var p; + + // Reset exposed scope values + setProps('inprogress', scope.$config.progress_class, false, undefined); + + // Check for supported types + p = promise.propertyIsEnumerable('promise') ? promise.promise : promise; + p = p instanceof Array ? p : [p]; + + $q.all(p) + .then(success, error, progress); + } + + // Handle sucessful promises + function success(value){ + setProps('success', scope.$config.success_class, true, scope.promise instanceof Array ? value : value[0]); + } + + // Handle promise errors + function error(err){ + setProps('error', scope.$config.error_class, true, err); + } + + // Handle promise notify (progress) + function progress(value){ + scope.$value = value; + } + + // Update exposed scope properties + function setProps(status, className, done, value){ + scope.$status = status; + scope.$class = className; + scope.$done = done; + scope.$value = value; + + // If done and delay is set, set a timeout to clear the class + if (scope.$config.delay > 0 && done) { + $timeout(function(){ + scope.$status = 'idle'; + scope.$class = scope.$config.idle_class; + }, scope.$config.delay); + } + } + + + /* + Event handling + */ + + // I DON'T LIKE THIS! + if (scope.$config.watch) { + var watch = scope.$watch('promise', function(promise){ + if (!promise) return; + + $timeout(function(){ + setPromise(promise); + }); + }); + + // Scope cleanup + scope.$on('$destroy', function(){ + // Clear the watch + watch(); + }); + } + + scope.$on('promise:status', function(){ + $timeout(function(){ + setPromise(scope.promise); + }); + }); + + + /* + Render + */ + + // Transclude the bar's content + //- Transclusion is done here and not with ngTransclude + //- because we want to expose some scope goodies back to + //- the original template from the directive's isolate scope. + transclude(scope, function(clone){ + element.append(clone); + }); + } + }; +}]) +; \ No newline at end of file