patternjavascriptMinor
UI selector directives for an AngularJS invoicing module
Viewed 0 times
selectorangularjsinvoicingmodulefordirectives
Problem
I'm wrapping ui-select in my own directives to pre-configure and style it, as I need it for my app in different select widgets. They appear on many pages, for example
What I really dislike is, that I'm mostly copy-and-pasting the directive's code and then just changing the service. What I would normally do is to create some kind of abstract or base class and inherit from that but classes and inheritance are a "problem" in Javascript for me. By the way, I'm aware the language is prototypical. So far I couldn't find a better way to avoid the code duplication between the directives.
Any recommendation how to make this more DRY?
```
angular.module('crm.invoicing').directive('productsSelect', [function() {
return {
restrict: 'E',
scope: {
productId: '=',
products: '=',
multiple: '=?',
onSelection: '=?',
onRemoval: '=?',
selectOptions: '=?',
list: '=?'
},
transclude: true,
templateUrl: '/psa/invoicing/templates/products-select.html',
controller: function($scope, ProductsService) {
$scope.multiple = angular.isDefined($scope.multiple) ? $scope.multiple : false;
if (!angular.isDefined($scope.selectOptions)) {
$scope.selectOptions = {};
};
var data = {
productId: $scope.productId,
products: $scope.products,
items: null,
selected: $scope.products
};
if ($scope.multiple === false) {
data.items = [data.selected]
} else {
data.selected = $scope.products;
data.items = []
}
$scope.productsList = {
items: data.items,
selected: data.selected,
select: function ($item, $model) {
$scope.p
accountSelect, countrySelect, contactSelect and so on.What I really dislike is, that I'm mostly copy-and-pasting the directive's code and then just changing the service. What I would normally do is to create some kind of abstract or base class and inherit from that but classes and inheritance are a "problem" in Javascript for me. By the way, I'm aware the language is prototypical. So far I couldn't find a better way to avoid the code duplication between the directives.
Any recommendation how to make this more DRY?
```
angular.module('crm.invoicing').directive('productsSelect', [function() {
return {
restrict: 'E',
scope: {
productId: '=',
products: '=',
multiple: '=?',
onSelection: '=?',
onRemoval: '=?',
selectOptions: '=?',
list: '=?'
},
transclude: true,
templateUrl: '/psa/invoicing/templates/products-select.html',
controller: function($scope, ProductsService) {
$scope.multiple = angular.isDefined($scope.multiple) ? $scope.multiple : false;
if (!angular.isDefined($scope.selectOptions)) {
$scope.selectOptions = {};
};
var data = {
productId: $scope.productId,
products: $scope.products,
items: null,
selected: $scope.products
};
if ($scope.multiple === false) {
data.items = [data.selected]
} else {
data.selected = $scope.products;
data.items = []
}
$scope.productsList = {
items: data.items,
selected: data.selected,
select: function ($item, $model) {
$scope.p
Solution
Anything that makes each instance of a directive different should be delegated somewhere higher up. You make a "wrapper" directive. Generic logic stays down the hierarchy, while specific logic wraps and provides for it.
Assuming each of your directive has a hierarchy of something like:
I suggest you actually create another directive that sits between your three different directives, and the ui-select. This directive contains your generic crud logic you wrote above but it does not have any special UI (it merely wraps ui-select) and knows nothing about services. It only knows that it is provided search results via
Now for the specific directives. You wrap the generic select with the custom template for each then supply the data via
In the end, it should look like:
Generic select - Wrapping your ui-select. All your logic stays on this directive. This is also where your logic interfaces with ui-select.
Your 3 different selects - Each of them have different templates and services. When generic-select does a search, it should call a function that's provided by the three (in this case, each will have
Assuming each of your directive has a hierarchy of something like:
page -> your three directives -> ui-selectI suggest you actually create another directive that sits between your three different directives, and the ui-select. This directive contains your generic crud logic you wrote above but it does not have any special UI (it merely wraps ui-select) and knows nothing about services. It only knows that it is provided search results via
$scope and is passed in functions that it can call when stuff happens..--- calls handler when something happens ---.
V |
page -> your directives -(data via $scope)-> generic select -> ui-selectNow for the specific directives. You wrap the generic select with the custom template for each then supply the data via
$scope. As for knowing when to search, the generic level should be passed a handler from the specific directives that it can call when search is done. Specific directive does lookup on service, updates data on $scope, done.In the end, it should look like:
Generic select - Wrapping your ui-select. All your logic stays on this directive. This is also where your logic interfaces with ui-select.
Your 3 different selects - Each of them have different templates and services. When generic-select does a search, it should call a function that's provided by the three (in this case, each will have
itemSearch). In that function, it calls the specific service, updating $scope which should then be transparent to the generic-select.
Code Snippets
page -> your three directives -> ui-select.--- calls handler when something happens ---.
V |
page -> your directives -(data via $scope)-> generic select -> ui-select<div class="generic-select">
<ui-select ... ></ui-select>
</div><div class="product-select>
<generic-select onSearch="itemSearch"></generic-select>
</div>
<div class="account-select>
<generic-select onSearch="itemSearch"></generic-select>
</div>
<div class="country-select>
<generic-select onSearch="itemSearch"></generic-select>
</div>Context
StackExchange Code Review Q#102599, answer score: 5
Revisions (0)
No revisions yet.