Twitter Bootstrap control-group directive
HTML
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<form ng-app="app" name="form" class="form-horizontal" ng-controller="ExampleController">
<div control-group label="Example">
<input type="text" id="example" name="example" ng-model="data.example" required />
</div>
</form>
JavaScript
var app = angular.module("app", []);
app.directive("controlGroupX", function () {
return {
template:
'<div class="control-group" ng-class="{ error: isError }">\
<label class="control-label" for="{{for}}">{{label}}</label>\
<div class="controls" ng-transclude></div>\
</div>',
replace: true,
transclude: true,
require: "^form",
scope: {
label: "@" // Gets the string contents of the `label` attribute
},
link: function (scope, element, attrs, formController) {
// The <label> should have a `for` attribute that links it to the input.
// Get the `id` attribute from the input element
// and add it to the scope so our template can access it.
var id = element.find(":input").attr("id");
scope.for = id;
// Get the `name` attribute of the input
var inputName = element.find(":input").attr("name");
// Build the scope expression that contains the validation status.
// e.g. "form.example.$invalid"
var errorExpression = [formController.$name, inputName, "$invalid"].join(".");
// Watch the parent scope, because current scope is isolated.
scope.$parent.$watch(errorExpression, function (isError) {
scope.isError = isError;
});
}
};
});
app.controller("ExampleController", ["$scope", function ($scope) {
$scope.data = {
example: ""
};
}]);