HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

UI selector directives for an AngularJS invoicing module

Submitted by: @import:stackexchange-codereview··
0
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 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:

page -> your three directives -> ui-select


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 $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-select


Now 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.