Hi i'm looking for an Angular.js example like this,
http://www.jqueryscript.net/form/Knob-Style-Rotary-Switch-Plugin-with-jQuery-rotarySwitch.html
Ideally a complete path from rotary switch to posting some commands to the service side, based in the switch position.
Any good example please ?
Otherwise i plan to write a angular module using this sample jquery file, every time the rotary switch moves to a new location, it's call $http.post to let service side know. The main module will use this rotary switch module. But really don't want to write my own.
Like mentioned in the comments I also think that there is probably no directive for that rotary switch plugin.
So something like in the demo below should work for this. I've just created a directive where I've initialized the jquery plugin inside the link method (dom is ready to manipulate inside postLink).
To reduce http traffic I've added a timeout so that the callback is called at a lower rate. (see rotKnob_delayedCallback in DEFAULT_OPTS)
You could write a directive for every jQuery plugin where you can't find a wrapper directive.
Please also have a look at the browser console. In jsfiddle you can see the post echo of the position in the console.
Here is also the fiddle to the demo below.
angular.module('demoApp', ['rotaryKnobModule'])
.factory('knobService', KnobService)
.controller('mainController', MainController)
angular.module('rotaryKnobModule', [])
.constant('DEFAULT_OPTS', {
// Minimal value
minimum: 0,
// Maximum value
maximum: 12,
// Step size
step: 1,
// Snap to steps in motion
snapInMotion: true,
// Start point in deg
beginDeg: 0,
// Length in deg
lengthDeg: 360,
// // Which value will used, if the the start and the end point at the same deg.
minimumOverMaximum: true,
// Show input element
showInput: false,
// Show deg marks
showMarks: false,
// Theme class
themeClass: 'defaultTheme',
// custom option for directive
rotKnb_delayedCallback: 500 // in ms
})
.directive('rotaryKnob', RotaryKnob);
function MainController(knobService, $timeout) {
var vm = this;
vm.position = 0;
vm.options = {
minimum: 0,
maximum: 100
};
vm.onChange = function(pos) {
console.log('current position change handler', pos);
knobService.postPos(pos).then(function(response){
console.log('success', response)
}, function(response) {
console.log('failed');
});
}
}
function KnobService($http) {
return {
postPos: function(pos) {
var data = $.param({
json: JSON.stringify({
position: pos
})
});
return $http.post('/echo/json/', data);
}
}
}
function RotaryKnob($timeout) {
return {
scope: {},
bindToController: {
pos: '=',
options: '=?',
onChange: '&?'
},
controllerAs: 'rotKnobCtrl',
template: '<input type="text" class="rotarySwitch"/>',
link: function(scope, element, attrs) {
var options = scope.rotKnobCtrl.options,
ctrl = scope.rotKnobCtrl,
pos = 0, changeTimeout;
//console.log(options);
var $rotKnob = $(element).rotaryswitch(options);
$rotKnob.on('change', function() {
pos = parseInt($(this).val());
scope.rotKnobCtrl.pos = pos;
if ( !changeTimeout ) // reduce update rate to server
changeTimeout = $timeout(function() {
ctrl.onChange({pos: pos});
changeTimeout = null;
}, options.rotKnb_delayedCallback);
scope.$apply();
})
},
controller: function(DEFAULT_OPTS) {
var self = this;
self.pos = 0;
//console.log(self.options);
self.options = angular.extend({}, DEFAULT_OPTS, self.options);
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdn.rawgit.com/r12r/com.redwhitesilver.rotarySwitch/master/jquery.rotaryswitch.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.js"></script>
<script src="https://cdn.rawgit.com/r12r/com.redwhitesilver.rotarySwitch/master/jquery.rotaryswitch.js"></script>
<div ng-app="demoApp" ng-controller="mainController as mainCtrl">
<rotary-knob
pos="mainCtrl.position"
options="mainCtrl.options"
on-change="mainCtrl.onChange(pos)"
></rotary-knob>
Current position {{mainCtrl.position}}
</div>
Related
I have a site that allows a person to import some data. They click a button, and the file select opens and they select a file. When they select a file I open a dialog that they can't close that tells them their data is being imported. Once I get the call back from the api call to import the file, I then close that dialog and open a new one that gives the status of the import.
On Chrome the "please wait while importing" dialog closes as expected. On IE it doesn't. If you use IE 11 it should happen in the following fiddle:
http://jsfiddle.net/og6qsxdw/
You see a ghost like outline of the dialog go up and fade away like its trying to close but the dialog and overlay still remain.
<div ng-controller="MyCtrl">
<input type="file" ng-simple-upload web-api-url="http://www.fakeresponse.com/api/?sleep=5" select-fn="importFileSelected" callback-fn="importDataComplete" />
<script type="text/ng-template" id="templateId">
<div>
Getting Data
</div>
</script>
</div>
JavaScript/AngularJS code:
var myApp = angular.module('myApp', ['ngDialog', 'ngSimpleUpload']);
function MyCtrl($scope, $http, ngDialog) {
$scope.importDataComplete = function() {
$scope.dlg.close();
}
$scope.importFileSelected = function() {
$scope.dlg = ngDialog.open({
template: 'templateId',
className: 'ngdialog-theme-default',
closeByDocument: false,
showClose: false
});
}
}
angular.module('ngSimpleUpload', [])
.directive('ngSimpleUpload', [function() {
return {
scope: {
webApiUrl: '#',
callbackFn: '=',
selectFn: '=',
buttonId: '#'
},
link: function(scope, element, attrs) {
// if button id value exists
if (scope.buttonId) {
$('#' + scope.buttonId).on('click', function() {
// retrieves files from file input
var files = element[0].files;
// will not fire until file(s) are selected
if (files.length == 0) {
console.log('No files detected.');
return false;
}
Upload(files);
});
} else {
// original code, trigger upload on change
element.on('change', function(evt) {
var files = evt.__files_ || (evt.target && evt.target.files);
Upload(files);
// removes file(s) from input
$(this).val('');
});
}
function Upload(files) {
var fd = new FormData();
angular.forEach(files, function(v, k) {
fd.append('file', files[k]);
});
// this tell us the user clicked open instead of cancel so we can start our overlay
scope.selectFn();
return $.ajax({
type: 'GET',
url: scope.webApiUrl,
async: true,
cache: false,
contentType: false,
processData: false
}).done(function(d) {
// callback function in the controller
scope.callbackFn(d);
}).fail(function(x) {
console.log(x);
});
}
}
}
}]);
Alright, so here's the deal. In IE, when you open the dialog, two instances are instantiated. When the upload completes, you have a reference to close the most recent dialog, but one existed milliseconds before as well.
I had originally thought at quick glance that this was just an ugly IE bug, and you had to "keep track" of the instances, however, I failed to take note of jQuery's involvment in your link function. Thusly, my initial solution was a hack/workaround, but better can be done.
It seems that the mixture of the two libraries is the culprit, where Angular and jQuery are not communicating properly. I've inserted a reference below to a ticket that discusses jQuery events with Angular.
jQuery and AngularJS: Bind Events to Changing DOM
Solution
My suggestion, as always in these cases, is not to leverage jQuery on top of Angular. It adds an additional layer of confusion, and requires you to be prudent about maintaining proper coupling between the two (in circumstances such as this).
I have provided a solution, where I clean up your link function. It uses a lot of your existing code, but with the absence of the jQuery bits. It seems to work just fine for me in both Chrome and IE now.
http://plnkr.co/edit/6Z4Rzg1Zm3w5rYyqQqmg?p=preview
link: function(scope, element, attrs) {
console.warn("Element is", element);
// original code, trigger upload on change
element.on('change', function(evt) {
var files = evt.__files_ || (evt.target && evt.target.files);
Upload(files);
});
function Upload(files) {
var fd = new FormData();
angular.forEach(files, function(v, k) {
fd.append('file', files[k]);
console.log("File loaded");
});
// this tell us the user clicked open instead of cancel so we can start our overlay
scope.selectFn();
$http({
url: scope.webApiUrl,
method: "GET",
cache: true
}).success(function(d, status, headers, config) {
scope.callbackFn(d);
}).error(function(data, status, headers, config) {
console.warn("Request failed...");
});
}
}
I'm working on an angular app which needs to display some data as a chart.
The library i'm using in nvd3.js.
The chart is a directive, and the code that creates the chart is the following:
angular.module('charts', []).factory('d3', [
function() {
return d3;
}
]).factory('nv', [
function() {
return nv;
}
]).directive('hgCharts', [
'$timeout', 'd3', 'nv', function($timeout, d3, nv) {
var linkFunction;
linkFunction = function(scope, element, attr) {
var chart, chartCreated, containerHeight, containerWidth, createChart, date, svg, xScale;
containerWidth = element[0].offsetWidth;
containerHeight = element[0].offsetHeight;
date = new Date;
svg = element.find('svg');
chart = void 0;
xScale = d3.time.scale().domain([date, date]).nice(d3.time.day).range([0, containerWidth - 50]);
createChart = function() {
debugger;
chart = nv.models.multiChart();
chart.options({
showLegend: true,
duration: 0,
margin: {
'left': 20,
'right': 30,
'top': 50,
'bottom': 50
},
yDomain1: [0, 30],
showXAxis: false,
showMinMax: false,
useVoronoi: true
});
chart.xAxis.scale(xScale).orient('bottom').ticks(3).duration(0).tickFormat(d3.time.format('%H:%M'));
chart.yAxis1.orient('left').ticks(7).duration(0);
chart.tooltip.enabled(true);
nv.utils.windowResize(function() {
containerWidth = element[0].offsetWidth;
containerHeight = element[0].offsetHeight;
chart.xAxis.range([0, containerWidth - 50]);
chart.update();
return d3.selectAll(".nv-axisMaxMin-x")[0][0].remove();
});
return chart;
};
chartCreated = function() {
console.log("Scope.data is: ", scope.data);
d3.select(svg[0]).attr('width', '100%').attr('height', '100%').datum(scope.data).call(chart);
return d3.selectAll(".nv-axisMaxMin-x")[0][0].remove();
};
scope.$watch(function() {
return scope.data;
}, function() {
if (scope.data) {
console.log("Scope data changed...");
return nv.addGraph(createChart, chartCreated);
}
});
};
return {
restrict: 'E',
scope: {
data: '='
},
template: '<svg></svg>',
link: linkFunction
};
}
]);
In my UI, I have a prev and next button.
Each of the buttons above have a function in the controller to get the right start and end time period using moment.js, and once they have the right times, the call a function.
This it the code for the funciton they call:
var loadChartData;
loadChartData = function(start, end) {
if (!start) {
start = 1467331200000;
}
if (!end) {
end = 1467417599000;
}
return hgChartData.loadData(start, end).then(function(res) {
return $scope.chartData = res;
});
};
The function calls a service to load my data, and returns them as a promise (res), and then creates (or modify) $scope.chartData with the new data.
Debugging this process a lot, I noticed that the problem I've got, is that even if $scope.chartData changes (I have the right result printed on my console), the debugger inside createChart function is never called, and so the chart with my new data is not created.
Am I doing something wrong with the watcher?
Strangely, it works only the first time I click on a button (it doesn't matter if it prev or next), but then it stops (as I said above, scope.chartData changes everytime according to the console messages I print.) The code for next and prev buttons are below:
var requiredDay;
requiredDay = '';
$scope.prevDay = function() {
if (!requiredDay) {
requiredDay = moment(1467377134000);
}
requiredDay = requiredDay.clone().subtract(1, 'day');
console.log("Required data period is: ", requiredDay);
displayRequestedData(requiredDay);
return requiredDay;
};
$scope.nextDay = function() {
if (!requiredDay) {
requiredDay = moment(1467377134000);
}
requiredDay = requiredDay.clone().add(1, 'day');
console.log("Required data period is: ", requiredDay);
displayRequestedData(requiredDay);
return requiredDay;
};
I think the problem is with my watcher, but I can't figure out what i'm doing wrong.
Thanks for any help
EDIT
After debugging this a bit more, I found out that the scope is actually watched, but as soon as I changes to empty array, it's not watched anymore.
I'll explain it a bit better:
scope.data (the scope i'm watching in my directive) is equal to an array of object, and each object has this structure (taken directly from console):
Object{
color:"red"
duration:0
key:"Temperature"
originalKey:"Temperature"
type:"line"
values:Array[10]
yAxis:1
}
Each array inside the previous object look like this:
Object{
series:0
x:1467330307000
y:20
}
what I found is that if my array changes its values (array values are the only thing that changes inside that scope), the scope is updated and the chart re-rendered, but as soon as I have no data for the requested time period, the number of arrays inside my object goes to 0 and then the watcher doesn't work anymore.
I hope this helps a bit more. I'm still having issues try to understand why and how to solve it.
Thanks again
I'm trying to build a custom directive to either show items on a page or completely remove them based on authorization data, and I'm clearly missing something, 'cuz its not working and I'm having a hard time figuring out why.
I've been following the guide here:
http://adamalbrecht.com/2014/09/22/authorization-with-angular-and-ui-router/
Which said to take a copy of the NgIf source code, and modify it (as I have below).
I'm really confused because not only is it not working as expected, but even when I put break-points within the function calls in the directive, it never hits those break points.
I'm not sure what I'm doing wrong. Is there something else not documented within the steps that I need to do in order to use a custom directive, or did I somehow miss a step? Regular ng-if's work on the same page just fine.
EDIT: I should note that AuthorizeService.isAuthorizedForOne returns a promise value which is either true or false. This works fine in other contexts.
'use strict';
/**
* #ngdoc directive
* #name ngIfPermission
* #restrict A
*
* #description
**/
var ngIfPermissionDirective = ['$animate', function($animate, AuthorizeService) {
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
console.log("I am here");
var block, childScope, previousElements;
$attr.$observe("ngIfPermission", function(value){
console.log("I am there");
var permissions = JSON.parse(value.replace(/'/g, '"'));
AuthorizeService.isAuthorizedForOne(permissions).then(function(auth){
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end ngIfPermission: ' + $attr.ngIfPermission + ' ');
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
},
function(err) {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes(block.clone);
$animate.leave(previousElements).then(function() {
previousElements = null;
});
block = null;
}
});
});
}
};
}];
How I'm referencing it:
<div ng-if-permission="['OOGY']">You can see this.</div>
<div ng-if-permission='["BOOGY"]'>or this</div>
I think you might got the declaration for the directive wrong.
app.directive( 'ngIfPermissionDirective', function($animate){
//directive here
));
DEMO http://plnkr.co/edit/BhubrfMAiW3K4ANI3pTx
I am new to angularjs, and I have been reading a ton of documentation and reading through various articles and tutorials as well as videos to help me figure this stuff out.
I am trying to get two directives to interchange information between themselves. a really simplified version of what i am trying to do is at odetocode (http://odetocode.com/blogs/scott/archive/2013/09/11/moving-data-in-an-angularjs-directive.aspx) where k scott allen has wrapped his directives with a div that has the ng-controller attribute it works beautifully.
I have a slightly more complex test I am working on, and I am trying to get it to work similarly to the code I have mentioned.
my two directives talk to each other when I list the ng-controller attribute in the actual template for each directive. it works, but I don't think it is correct. the actual controller code is run twice, once for each directive.
when I move the controller into the div that wraps the two directives, the two directives stop interacting (the change event in the location-selector template doesn't change the park in the controller). I am pretty sure it has something to do with the scope. if anyone can point me in the right direction, or where I should look for information, that would be much appreciated.
here is my fiddle showing my code http://jsfiddle.net/jgbL9/25/
<div ng-app="myApp">
<location-selector ></location-selector ><br/>
<portal-map ></portal-map >
</div>
var App = angular.module('myApp', ['ngResource']);
App.directive('locationSelector',['parkList', function(parkList) {
return {
restrict: 'E',
scope: {
parkId : '=',
parkName : '='
},
template: '<select ng-controller="portalMapCtrl"'+
' ng-model="listParks" ng-change="changePark()" '+
' park-id="parkId" park-name="parkName" ' +
' ng-options="park as park.attributes.NAME for park in Parks" >'+
'</select>',
link: function (scope,element,attrs){
parkList.getListFromGIS().success(function(data) {
scope.Parks = data.features;
});
}
};
}]);
App.directive('portalMap', function(){
return {
restrict: 'E',
scope:{
parkId: "=",
parkName: "="
},
template: '<style type="text/css" media="screen">'+
'#mapCanvas {height: 500px; width:75%; border:thin black solid; }'+
'</style>'+
'<div id="mapCanvas" park-id="parkId" park-name="parkName" ng-controller="portalMapCtrl" ></div>'
}
});
App.controller('portalMapCtrl',['$scope','parkList', function( $scope, parkList ){
var map = {};
var STREETMAPSERVICE = "https://gis.odot.state.or.us/ArcGIS/rest/services/BASEMAPS/Basemap_Streets/MapServer";
var FOTOSSERVICE = "https://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer?f=jsapi";
var UTILSSERVICE = "http://gis.prd.state.or.us/ArcGIS/rest/services/OPRDAssets/MapServer";
var UTILSSERVICE_PARKLAYER = 0;
var UTILSSERVICE_STRUCTUREPOLY = 7;
var UTILSSERVICE_SURFACE = 11;
var UTILSSERVICE_PARCELS = 12;
var timer;
var ALL_LAYERS = [UTILSSERVICE_PARKLAYER,UTILSSERVICE_STRUCTUREPOLY,UTILSSERVICE_SURFACE,UTILSSERVICE_PARCELS];
$scope.parkId = 0;
$scope.parkName = "";
$scope.changePark = function (){
require(["esri/SpatialReference","esri/geometry/Polygon"],
function(SpatialReference,Polygon){
console.log('change park');
$scope.parkId = $scope.listParks.attributes.PARK_HUB_ID;
$scope.parkName = $scope.listParks.attributes.NAME;
parkList.getParkFromGIS($scope.parkId).then(function(data){
var x = data.data;
var y = x.features[0];
var rings = y['geometry'];
var poly = new Polygon(rings);
var xtnt = poly.getExtent();
var sr = new SpatialReference({wkid:2992});
xtnt.setSpatialReference (sr);
map.setExtent(xtnt,true);
});
});
};
function addService(srvc, srvcType, lyrId){require([
"esri/layers/ArcGISTiledMapServiceLayer",
"esri/layers/ArcGISDynamicMapServiceLayer",
"esri/layers/ImageParameters"], function(Tiled,Dynamic,Parameters){
var mapService = {};
if(srvcType == 'Tiled'){
mapService = new Tiled(srvc);
}else{
var imageParameters = new Parameters();
imageParameters.layerIds = lyrId;
imageParameters.transparent = true;
mapService = new Dynamic(srvc,{"imageParameters":imageParameters});
}
map.addLayer(mapService);
});
}
function createMap(){
require(["esri/map"],function(Map){
console.log('create map');
map = new Map("mapCanvas");
addService(STREETMAPSERVICE,'Tiled');
addService(FOTOSSERVICE,'Tiled');
addService(UTILSSERVICE,'Dynamic',ALL_LAYERS);
});
}
createMap();
}]);
App.factory('parkList',['$http', function($http) {
return {
getListFromGIS: function() {
var myUrl = 'http://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer/0/query?where=OBJECTID+%3E+0&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&outFields=PARK_HUB_ID%2CNAME&returnGeometry=false&&returnIdsOnly=false&returnCountOnly=false&orderByFields=NAME&returnZ=false&returnM=false&returnDistinctValues=true&f=pjson&callback=JSON_CALLBACK';
return $http ({ url: myUrl, method: 'JSONP'});
},
getParkFromGIS: function (id){
var myUrl = "http://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer/0/query?where=PARK_HUB_ID%3d"+id+"&f=pjson&callback=JSON_CALLBACK";
return $http ({ url: myUrl, method: 'JSONP'});
},
JSON_CALLBACK: function(data) {}
};
}]);
(this is the code working with the ng-controller listed in the template of each directive).
any other comments or suggestions you would like to offer about my code structure or code choices will be appreciated also, as I mentioned, I am learning, and the more I can learn the more fun I will have coding.
I believe you're having problems due to your isolate scopes on your directives (the scope: { } in your link function).
I would suggest inlining the templates into your application, instead of trying to make them directives.
In particular, the locationSelector is going to be hard to make a directive - it's usually easier to have your input elements be a part of the element their controller is on.
If you did want to have them be a directive, I'd suggest passing the listParts value into the changePark function:
<select ... ng-change="changePark(listParks)" ...>
I'm sure this is going to be a "dont do that!" but I am trying to display the style on an angular element.
<div ng-repeat="x in ['blue', 'green']" class="{{x}}">
<h3 insert-style>{{theStyle['background-color']}}</h3>
</div>
Result would be
<div class='blue'><h3>blue(psudeo code hex code)</h3></div>
<div class='green'><h3>green(psudeo code hex code)</h3></div>
I basically need to get the style attributes and display them.
Directive Code...
directives.insertStyle = [ function(){
return {
link: function(scope, element, attrs) {
scope.theStyle = window.getComputedStyle(element[0], null);
}
}
}];
Fiddle example: http://jsfiddle.net/ncapito/G33PE/
My final solution (using a single prop didn't work, but when I use the whole obj it works fine)...
Markup
<div insert-style class="box blue">
<h4 > {{ theStyle['color'] | toHex}} </h4>
</div>
Directive
directives.insertStyle = [ "$window", function($window){
return {
link: function(scope, element, attrs) {
var elementStyleMap = $window.getComputedStyle(element[0], null);
scope.theStyle = elementStyleMap
}
}
}];
Eureka!
http://jsfiddle.net/G33PE/5/
var leanwxApp = angular.module('LeanwxApp', [], function () {});
var controllers = {};
var directives = {};
directives.insertStyle = [ function(){
return {
link: function(scope, element, attrs) {
scope.theStyle = window.getComputedStyle(element[0].parentElement, null)
}
}
}];
leanwxApp.controller(controllers);
leanwxApp.directive(directives);
So that just took lots of persistence and guessing. Perhaps the timeout is unnecessary but while debugging it seemed I only got the style value from the parent after the timeout occurred.
Also I'm not sure why but I had to go up to the parentElement to get the style (even though it would realistically be inherited (shrug)?)
Updated fiddle again
Did one without the timeout but just looking at the parentElement for the style and it seems to still work, so scratch the suspicions about the style not being available at all, it's just not available where I would expect it.
Also holy cow there are a lot of ways to debug in Chrome:
https://developers.google.com/chrome-developer-tools/docs/javascript-debugging
I used
debugger;
statements in the code to drop in breakpoints without having to search all the fiddle files.
One more quick update
The code below comes out of Boostrap-UI from the AngularUI team and claims to provide a means to watch the appropriate events (haven't tried this but it looks like it should help).
http://angular-ui.github.io/bootstrap/
/**
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
* #param {DOMElement} element The DOMElement that will be animated.
* #param {string|object|function} trigger The thing that will cause the transition to start:
* - As a string, it represents the css class to be added to the element.
* - As an object, it represents a hash of style attributes to be applied to the element.
* - As a function, it represents a function to be called that will cause the transition to occur.
* #return {Promise} A promise that is resolved when the transition finishes.
*/
.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
var $transition = function(element, trigger, options) {
options = options || {};
var deferred = $q.defer();
var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
var transitionEndHandler = function(event) {
$rootScope.$apply(function() {
element.unbind(endEventName, transitionEndHandler);
deferred.resolve(element);
});
};
if (endEventName) {
element.bind(endEventName, transitionEndHandler);
}
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
$timeout(function() {
if ( angular.isString(trigger) ) {
element.addClass(trigger);
} else if ( angular.isFunction(trigger) ) {
trigger(element);
} else if ( angular.isObject(trigger) ) {
element.css(trigger);
}
//If browser does not support transitions, instantly resolve
if ( !endEventName ) {
deferred.resolve(element);
}
});
// Add our custom cancel function to the promise that is returned
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
// i.e. it will therefore never raise a transitionEnd event for that transition
deferred.promise.cancel = function() {
if ( endEventName ) {
element.unbind(endEventName, transitionEndHandler);
}
deferred.reject('Transition cancelled');
};
return deferred.promise;
};
// Work out the name of the transitionEnd event
var transElement = document.createElement('trans');
var transitionEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'transition': 'transitionend'
};
var animationEndEventNames = {
'WebkitTransition': 'webkitAnimationEnd',
'MozTransition': 'animationend',
'OTransition': 'oAnimationEnd',
'transition': 'animationend'
};
function findEndEventName(endEventNames) {
for (var name in endEventNames){
if (transElement.style[name] !== undefined) {
return endEventNames[name];
}
}
}
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
return $transition;
}]);
The issue you'll face is that getComputedStyle is considered a very slow running method, so you will run into performance issues if using that, especially if you want angularjs to update the view whenever getComputedStyle changes.
Also, getComputedStyle will resolve every single style declaration possible, which i think will not be very useful. So i think a method to reduce the number of possible style is needed.
Definitely consider this an anti-pattern, but if you still insist in this foolishness:
module.directive('getStyleProperty', function($window){
return {
//Child scope so properties are not leaked to parent
scope : true,
link : function(scope, element, attr){
//A map of styles you are interested in
var styleProperties = ['text', 'border'];
scope.$watch(function(){
//A watch function to get the styles
//Since this runs every single time there is an angularjs loop, this would not be a very performant way to do this
var obj = {};
var computedStyle = $window.getComputedStyle(element[0]);
angular.forEach(styleProperties, function(value){
obj[value] = computedStyle.getPropertyValue(value);
});
return obj;
}, function(newValue){
scope.theStyle = newValue;
});
}
}
});
This solution works if you don't HAVE to have the directive on the child element. If you just place the declaration on the ng-repeat element itself, your solution works:
<div insert-style ng-repeat="x in ['blue', 'green']" class="{{x}}">
Fiddle