Answer the question
In order to leave comments, you need to log in
How to correctly extend a directive in AngularJS?
I have a task to make a custom select. With optional multi-select option.
Having started writing, I realized that almost the code for a regular select practically does not intersect with the code for a multiselect. In addition to very basic things, like expanding the list, etc.
To make the code easier to read, it was decided to divide them into 2 directives.
The first directive is basic:
dropdownlist - it implements expanding/collapsing the drop-down list, selecting an element from this list, and saving 1 element to the model.
and a second multiselect directive that overrides the behavior of the first.
filterApp.directive('dropdownList', function() {
return {
restrict: 'E',
require: '^ngModel',
scope: {
items: '=',
textField: '@',
valueField: '@',
ngModel: '='
},
templateUrl: filterAppUrl + '/partials/dropdownListTtpl.html',
controller: function($scope, $element, $attrs) {
this.scope = $scope;
this.valueField = $scope.valueField.toString().trim();
this.textField = $scope.textField.toString().trim();
this.ddmenu = $element.find('.dropdown-menu');
$scope.isOpen = false;
$scope.open = function() {
$scope.isOpen = true;
$('body').on('click.ddmenu', function(event) {
if ($(event.target).closest(this.ddmenu).length == 0) {
$scope.close();
$scope.$apply();
}
});
};
$scope.close = function() {
$scope.isOpen = false;
$('body').off('click.ddmenu');
};
$scope.toggle = function($event) {
$event.stopPropagation();
$scope.isOpen ? $scope.close() : $scope.open();
};
$scope.getItemName = function(item) {
return item[this.textField];
};
$scope.isSelected = function(_item) {
return typeof ($scope.ngModel) != "undefined" && $scope.ngModel &&
_item[this.valueField].toString() == $scope.ngModel.toString();
};
$scope.setLabel = function() {
if (typeof ($scope.ngModel) == "undefined" || !$scope.ngModel || $scope.ngModel.length < 1) {
$scope.currentItemLabel = $attrs.defaultText;
} else {
$scope.currentItemLabel = $scope.ngModel[this.textField].toString();
}
};
$scope.selectVal = function(_item) {
console.log($scope.ngModel);
if (typeof ($scope.ngModel) != "undefined" && $scope.ngModel) {
$scope.ngModel = _item;
}
$scope.setLabel();
//ngModelCtrl.$setViewValue($scope.ngModel);
$scope.close();
};
$scope.setLabel();
},
link: function($scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$render = function(){
$scope.setLabel();
} ;
//loading
$scope.$watch('items', function(data){
$scope.showLoading = (data.$resolved != undefined) & !data.$resolved;
}, true);
}
};
});
filterApp.directive('multiselect', function() {
return {
restrict: 'A',
require: ['dropdownList', '^ngModel'],
link: function(scope, element, attrs, ctrls) {
var ddListCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];
var scope = ddListCtrl.scope;
scope.isMultiSelect = true;
var valueField = ddListCtrl.valueField;
var textField = ddListCtrl.textField;
scope.selectVal = function(_item) {
var found = false;
if (typeof (scope.ngModel) != "undefined" && scope.ngModel) {
for (var i = 0; i < scope.ngModel.length; i++) {
if (!found) {
if (_item[valueField].toString() === scope.ngModel[i][valueField].toString()) {
found = true;
var index = scope.ngModel.indexOf(scope.ngModel[i]);
scope.ngModel.splice(index, 1);
}
}
}
} else {
scope.ngModel = [];
}
if (!found) {
scope.ngModel.push(_item);
}
scope.setLabel();
ngModelCtrl.$setViewValue(scope.ngModel);
};
scope.setLabel = function() {
if (typeof (scope.ngModel) =="undefined" || !scope.ngModel || scope.ngModel.length < 1) {
scope.currentItemLabel = attrs.defaultText;
} else {
var allItemsString = '';
var selectedItemsCount = scope.ngModel.length;
if (selectedItemsCount < 3) {
var itemsStrings = [];
angular.forEach(scope.ngModel, function(item) {
itemsStrings.push(item[textField].toString())
});
allItemsString = itemsStrings.join(" ,");
} else {
allItemsString = selectedItemsCount + " выбрано";
}
scope.currentItemLabel = allItemsString;
}
};
scope.setLabel();
scope.isSelected = function(_item) {
var found = false;
angular.forEach(scope.ngModel, function(item) {
if (!found) {
if (_item[valueField].toString() === item[valueField].toString()) {
found = true;
}
}
});
return found;
};
scope.cancelClose = function($event) {
$event.stopPropagation();
};
}
};
});
<!--С мультиселектом-->
<dropdown-list multiselect ng-model="nights" items="nightsItems" text-field="Name" default-text="все" value-field="name" ></dropdown-list>
<!--Без-->
<dropdown-list ng-model="departureCities" items="departureCityItems" default-text="все" text-field="Name" value-field="name" ></dropdown-list>
Answer the question
In order to leave comments, you need to log in
It is not necessary to throw higher controllers into the directive; if you need a specific function, and it is different in each multiselect, forward it as a parameter to the scope of the directive; it makes sense to remove all the element selection logic from the directive with a drop-down list, and implement it purely on the side of the "multiselect" directive.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question