Dynamic data-test from opower
Our QA relies on data-test- attributes for their automated testing. In our angular app, we wanted to be able to set data-test- attributes dynamically. However, angular doesn't let you put angular expressions in the name of html attributes. Thus, we developed op-attr (op- is our internal equivalent of ng-.)
by angularjsdc
HTML
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.1/angular.min.js"></script>
<div ng-app="myApp">
<span op-attr="{name: 'data-test-' + key, value: value}">
{{ 'greeting.prefix' | resolveMessageProperty }}{{value}}
</span>
</div>
JavaScript
angular.module('opower', []).directive('opAttr',
function() {
return {
link: function(scope, elm, attrs) {
// `attrs.opAttr` is the value that the user specified as
// op-attr="{foo: bar}"
// We want to watch for that value changing, so we can
// update the element we're on accordingly.
// When we specify a function as an argument to $watch,
// that function will be called on every digest,
// so it must be fast and idempotent.
scope.$watch(function() {
// scope.$eval will evaluate `attrs.opAttr`
// as an angular expression, given `scope`
// as the context
return scope.$eval(attrs.opAttr);
},
// `scope.$watch` passes the new value and old value
// of evaluating our watch function
// so we know what to add and remove
function(newVal, oldVal) {
// coerce oldVals and newVals into a singleton array
// if they are just a single value
var oldVals = [].concat(oldVal),
newVals = [].concat(newVal);
// wipe out the old attrs
angular.forEach(oldVals, function(attr) {
angular.element(elm[0]).removeAttr(attr.name, '');
});
// add the new attrs
angular.forEach(newVals, function(attr) {
if (!angular.isDefined(attr.pred) || attr.pred) {
angular.element(elm[0]).attr(attr.name, attr.value);
}
});
// !! It is important to pass `true` here,
...