I have a custom dropdown directive that has common attributes such as class and ng-model.
I have decided to extend this control for support for validation and now need to include optional attributes that should only get included in the output template if they are set by the programmer.
Sample
I have a partially working system in which I moved my code out of a template URL and into a string concatenation which I call in the post: function of the directives compile.
I would have preferred to leave my directives HTML in a template, but could not get that working so I have this solution.
Questions:
- Is this the best way to write a template with dynamic attributes?
- Could this be made to work while leaving the HTML in a template URL
- Should I be using the compile => post function or should this be done in the link function
Code for Directive
'use strict';
angular.module(APP)
.directive('wkKeyLabelSelect', ["$compile",
function($compile) {
return {
restrict: 'EA',
replace: true,
scope: {
'class': '@', // Permanent - One Way Attribute
ngModel: '=', // Permanent - Two Way Attribute (Angular)
items: '=', // Permanent - Two Way Attribute (Custom)
id: '@', // Dynamic - One Way Attribute
name: '@', // Dynamic - One Way Attribute
ngRequired: '=', // Dynamic - Two Way Attribute (Angular)
},
//templateUrl: COMPONENTS_PATH + '/keyLabelSelect/keyLabelSelect.html',
controller: 'KeyLabelSelectController',
link: function (scope, element, attrs) {
//$compile(element)(scope);
},
compile: function (element, attrs) {
// name & ngRequired are not available in the compile scope
//element.replaceWith($compile(html)(scope));
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
// Template goes here
var html =
'<select ' +
' class="{{class}}"' +
(scope.id ? ' id="{{id}}"' : "") +
(scope.name ? ' name="{{name}}"' : "") +
(scope.ngRequired ? ' ng-required="true"' : "") +
' ng-model="ngModel"' +
' ng-options="item.key as item.label for item in items"' +
'>' +
'</select>';
iElement.replaceWith($compile(html)(scope));
}
}
}
};
}
]);
Code for Directive Controller
angular.module(APP)
.controller('KeyLabelSelectController', ['$scope', function ($scope) {
$scope.klass = typeof $scope.klass === 'undefined' ? 'form-control' : $scope.klass;
console.log($scope.ngModel);
console.log($scope.items);
}]);
HTML used to run the directive
<div class="form-group" ng-class="{ 'has-error': editForm.state.$touched && editForm.name.$invalid }">
<label class="col-md-3 control-label">State</label>
<div class="col-md-9">
<wk-key-label-select id="state" name="state"
ng-required="true"
ng-model="model.entity.state"
class="form-control input-sm"
items="model.lookups.job_state">
</wk-key-label-select>
<div class="help-block" ng-messages="editForm.state.$error">
<p ng-message="required">Job State is required.</p>
</div>
</div>
</div>
My Original Template URL content, not used currently
<!-- This is now deprecated in place of inline string -->
<!-- How could I use a in place of string concatenation -->
<select class="{{klass}}"
name="{{name}}"
ng-model="ngModel"
ng-options="item.key as item.label for item in items"></select>


ngModelControllerproperly - doingscope: {ngModel: "="}is the wrong way to go.ngModelmodel (i.e. support other validators, custom parsers, integrate with<form>, etc), then it's a good idea to usengModelController. You don't have to, but that's the most Angular-way-ish. Overall, you're asking an XY question - instead, explain what you are trying to achieve with your custom dropdown that normal<select>cannot provide