AngularJS / Bootstrap 3 / jQuery "Growl" Directive v1.1
This version uses jQuery for the fadeOut and Bootstrap for the demo.
A Pen by Captain Anonymous on CodePen.
AngularJS / Bootstrap 3 / jQuery "Growl" Directive v1.1
This version uses jQuery for the fadeOut and Bootstrap for the demo.
A Pen by Captain Anonymous on CodePen.
<head> | |
<!-- jQuery --> | |
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> | |
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js'></script> | |
<link rel='stylesheet prefetch' href='http://netdna.bootstrapcdn.com/bootstrap/3.0.1/css/bootstrap.min.css'> | |
</head> | |
<body ng-app="myApp" ng-controller="myController"> | |
<div class="panel panel-default"> | |
<div class="panel-heading"> | |
<h3 class="panel-title">{{title}}</h3> | |
</div> | |
<div class="panel-body"> | |
<p> | |
These are the four types of "growls": | |
</p> | |
<p> | |
<span ng-repeat="item in types"><button class="btn btn-{{item}}" ng-click="growlAdd(item)" data-test="test">{{item | capitalize}}</button></span> | |
</p> | |
<p> | |
Timeout (ttl): <input ng-model="ttl" /><br/> | |
</p> | |
<p> | |
Position: <input ng-model="position" /><br/> | |
</p> | |
<p> | |
Text: <input ng-model="text" /><br/> | |
</p> | |
<p> | |
<ul> | |
<h4>Required for Release</h4> | |
<li class="done">popup "growl" with id, type, title, text √</li> | |
<li class="done">click-to-close: entire element or (x) √</li> | |
<li class="done">ttl timeout √</li> | |
<li class="done">settable text √</li> | |
<h4>Extras</h4> | |
<li class="done">four positions √</li> | |
<li>fadeOut/fadeIn (switching to css animation)</li> | |
<li>default bootstrap + custom styles</li> | |
<li>http intercept to auto show server messages</li> | |
</ul> | |
</p> | |
<h4>Growl Queue</h4> | |
<pre> | |
{{growls}} | |
</pre> | |
<span><button class="btn" ng-click="clear()">Clear Messages</button></span> | |
<!-- growls container grows with content --> | |
<div growls-container id="growls-container" class="{{position}}"></div> | |
</div> | |
</div> | |
</body> |
var lb = "• "; | |
var myApp = angular.module("myApp", []); | |
myApp.controller('myController', function($scope, $timeout) { | |
$scope.title = 'AngularJS / Bootstrap 3 "Growl" Directive v1.1'; | |
$scope.types = ["success", "info", "warning", "danger"]; | |
$scope.growlsClear = function (){ | |
$scope.growls = []; | |
}; | |
$scope.growlsClear(); | |
// defaults | |
$scope.ttl = 3000; | |
$scope.position = "bottom-right"; | |
$scope.text = "sample text"; | |
// Add | |
$scope.growlAdd = function(){ | |
//console.log(this); | |
var gid = $scope.growls.length + 1; | |
var type = this.item; | |
var title = this.item; | |
var text = $scope.text; | |
var item = {gid:gid, type:type, title:title, text:text}; | |
$scope.growls.push(item); | |
console.log(lb + "added " + type + " growl with gid " + gid) | |
}; | |
// Remove | |
$scope.growlRemove = function(gid) { | |
console.log(lb + 'growlRemove in controller removing gid ' + gid); | |
for (var i=0; i < $scope.growls.length; i++){ | |
if ($scope.growls[i].gid == gid) { | |
$scope.growls.splice(i,1); | |
break; | |
} | |
} | |
}; | |
}); | |
myApp.directive('growlsContainer', function() { | |
return { | |
template: [ | |
' <div growl-item class="growl-item {{growl.type}}" ng-repeat="growl in growls" data-gid="{{growl.gid}}">', | |
' <div class="growl-content">', | |
' <h4>{{growl.title | capitalize}}</h4>', | |
' <p>{{growl.text}}</p>', | |
' </div>', | |
' <div class="growl-close" data-gid="{{growl.gid}}""></div>', | |
' </div>' | |
].join('\n'), | |
}; | |
}); | |
myApp.directive("growlItem", function($timeout) { | |
return function($scope, $element, $attrs) { | |
var gid = $attrs.gid; | |
function growlFadeOut(gid){ | |
$($element).fadeOut(1000, function() { | |
console.log(lb + "growlFadeOut completed in directive calling growlRemove gid " + gid); | |
/* MUST wrap this in apply or digest will not happen */ | |
$scope.$apply( function(){ | |
$scope.growlRemove(gid); | |
}); | |
}); | |
}; | |
$element.bind("click", function() { | |
console.log(lb + "click fading gid: "+ gid); | |
growlFadeOut(gid); | |
}); | |
var promise = $timeout(function(){ | |
console.log(lb + 'timeout fading gid ' + gid); | |
growlFadeOut(gid); | |
},$scope.ttl); | |
$element.bind('$destroy', function() { | |
if(promise){ | |
console.log(lb + 'cancelling timeout for gid ' + gid) | |
//console.log(promise); | |
var cancelled = $timeout.cancel(promise) ? "timeout cancel success for gid " + gid : "timeout already expired for gid " + gid; | |
console.log(lb + cancelled); | |
}; | |
}); | |
}; | |
}); | |
myApp.filter('capitalize', function() { | |
return function(input, scope) { | |
if (input!=null){ | |
return input.substring(0,1).toUpperCase()+input.substring(1); | |
} | |
} | |
}); |
.panel { | |
width:80%; | |
margin:2em auto; | |
} | |
p { | |
padding:1em 1em 1em 1em ; | |
} | |
span { | |
margin:4px; | |
} | |
input { | |
float:right; | |
width:50%; | |
} | |
.done{ | |
font-weight:bold; | |
} | |
.growl-item p { | |
padding:0; | |
} | |
.top-left { top: 10px; left: 15px; } | |
.top-right { top: 10px; right: 15px; } | |
.bottom-left { bottom: 10px; left: 15px; } | |
.bottom-right { bottom: 10px; right: 15px; } | |
#growls-container { | |
width:300px; | |
position:fixed; | |
} | |
.growl-item { | |
background: #DDD; | |
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #F9F9F9), color-stop(1, #D5D5D5)); | |
background: -moz-linear-gradient(top, #F9F9F9, #D5D5D5); | |
background: -o-linear-gradient(#F9F9F9, #D5D5D5); | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F9F9F9', endColorstr='#D5D5D5'); | |
width: 300px; | |
font-size: 11px; | |
color: #333; | |
padding: 0; | |
margin: 0 0 10px 0; | |
border: 1px solid #A8A8A8; | |
position: relative; | |
-webkit-box-shadow: 0 0 7px rgba(0,0,0,.3); | |
-moz-box-shadow: 0 0 7px rgba(0,0,0,.3); | |
-o-box-shadow: 0 0 7px rgba(0,0,0,.3); | |
box-shadow: 0 0 7px rgba(0,0,0,.3); | |
-webkit-text-shadow: 0 0 1px #FFF; | |
-moz-text-shadow: 0 0 1px #FFF; | |
-o-text-shadow: 0 0 1px #FFF; | |
text-shadow: 0 0 1px #FFF; | |
-webkit-border-radius: 5px; | |
-moz-border-radius: 5px; | |
-o-border-radius: 5px; | |
border-radius: 5px; | |
} | |
.growl-content h4 { | |
font-weight:bold; | |
font-size: 12px; | |
color: #333; | |
margin-bottom: .5em; | |
-webkit-text-shadow: 0 0 1px #FFF; | |
-moz-text-shadow: 0 0 1px #FFF; | |
-o-text-shadow: 0 0 1px #FFF; | |
text-shadow: 0 0 1px #FFF; | |
} | |
.growl-content { padding: 10px 20px 10px 10px; } | |
.growl-close:hover { opacity: 1; cursor: pointer; } | |
.growl-close { | |
background: url(http://paulbhartzog.org/codepen/msgGrowl_close.png) no-repeat 50% 50%; | |
width: 11px; | |
height: 10px; | |
position: absolute; | |
top: 10px; | |
right: 10px; | |
opacity: .4; | |
border:1px solid gray; | |
padding:5px; | |
-webkit-border-radius: 2px; | |
-moz-border-radius: 2px; | |
-o-border-radius: 2px; | |
border-radius: 2px; | |
} | |
.growl-item.success .growl-content { background: url(http://paulbhartzog.org/codepen/growl_success.png) no-repeat 10px 13px; } | |
.growl-item.success .growl-content { padding-left: 50px; } | |
.growl-item.success h4 { color: #5B7027; } | |
.growl-item.danger .growl-content { background: url(http://paulbhartzog.org/codepen/growl_danger.png) no-repeat 10px 13px; } | |
.growl-item.danger .growl-content { padding-left: 50px; } | |
.growl-item.danger h4 { color: #AF4434; } | |
.growl-item.info .growl-content { background: url(http://paulbhartzog.org/codepen/growl_info.png) no-repeat 10px 13px; } | |
.growl-item.info .growl-content { padding-left: 50px; } | |
.growl-item.info h4 { color: #316AB7; } | |
.growl-item.warning .growl-content { background: url(http://paulbhartzog.org/codepen/growl_warning.png) no-repeat 10px 13px; } | |
.growl-item.warning .growl-content { padding-left: 50px; } | |
.growl-item.warning h4 { color: #B69201; } |