I saw a really good example of a AngularJS Bootstrap type of Select directive:
http://jsfiddle.net/cojahmetov/3DS49/
This meets most of my needs but it uses jQuery and we are not using that library.
The jQuery used in this example is very minimal but I do not know how I could replace the element locators that look like this:
Can anyone give me any pointers as to how I could replace this:
switch (attrs.menuType) {
case "button":
$('button.button-label', element).html(item.name);
break;
default:
$('a.dropdown-toggle', element).html('<b class="caret"></b> ' + item.name);
break;
}
So that it would work without jQuery
? Ideally I am hoping that someone knows enough to maybe come up with a version based on the version in the ui-bootstrap
. Perhaps this could even be something that might be added to ui-bootstrap for others to use.
Idea in that Fiddle was nice but I found the implementation kinda messy. So what can one do? Well, write an alternative dropdown
directive on top of ui-bootstrap
, of course!
Hope you find this helpful. It should be really easy to use.
Usage
<dropdown is-button
ng-model="vm.item"
items="vm.items"
callback="vm.callback(item)">
</dropdown>
So you pass in the ng-model
which holds the initial selection, if any. New value is set from the directive. In items
you have collection of id-name
pairs to choose from and a callback
function if you need it. If you specify the is-button
attribute, you'll get a button-stylish dropdown control.
Then the controller could look like the following.
Controller
// Controller
app.controller('Ctrl', function() {
var vm = this;
// items collection
vm.items = [{
id: 0,
name: 'London'
},{
id: 1,
name: 'Paris'
},{
id: 2,
name: 'Milan'
}];
// current item
vm.item = null; // vm.items[1];
// directive callback function
vm.callback = function(item) {
vm.fromCallback = 'User selected ' + angular.toJson(item);
};
});
Logic for the dropdown
directive is pretty simple, really.
Directive javaScript
// Dropdown directive
app.directive('dropdown', function() {
return {
restrict: 'E',
require: '^ngModel',
scope: {
ngModel: '=', // selection
items: '=', // items to select from
callback: '&' // callback
},
link: function(scope, element, attrs) {
element.on('click', function(event) {
event.preventDefault();
});
scope.default = 'Please select item';
scope.isButton = 'isButton' in attrs;
// selection changed handler
scope.select = function(item) {
scope.ngModel = item;
if (scope.callback) {
scope.callback({ item: item });
}
};
},
templateUrl: 'dropdown-template.html'
};
});
Directive HTML template
<div class="btn-group" dropdown>
<!-- button style dropdown -->
<button ng-if="isButton"
type="button"
class="btn btn-primary"
ng-bind="ngModel.name || default">
</button>
<button ng-if="isButton"
type="button"
class="btn btn-primary dropdown-toggle"
dropdown-toggle>
<span class="caret"></span>
</button>
<!-- no button, plz -->
<a ng-if="!isButton"
class="dropdown-toggle"
dropdown-toggle href
ng-bind="ngModel.name || default">
</a>
<span ng-if="!isButton" class="caret"></span>
<!-- dropdown items -->
<ul class="dropdown-menu" role="menu">
<li ng-repeat="item in items">
<a href="#"
ng-bind="item.name"
ng-click="select(item)"></a>
</li>
</ul>
</div>
Here's the look & feel of the initial implementation. In addition to the sample you provided, you'll get back the whole object and not just the id
.
This solution only supports IE9+ if you need to support legacy browsers I can find another solution and update.
http://jsfiddle.net/3DS49/160/
scope.selectVal = function (item) {
function selector(sel, htmlCont)
{
var elems = document.querySelectorAll(sel);
Array.prototype.forEach.call(elems, function(el, i){
el.innerHTML = htmlCont;
});
}
switch (attrs.menuType) {
case "button":
selector('button.button-label', item.name);
break;
default:
selector('a.dropdown-toggle', '<b class="caret"></b> ' + item.name);
break;
}
scope.doSelect({
selectedVal: item.id
});
};
Edit: I was right, you don't need to use .html() in this example, and is a bad practice in general. Notice how I used the scope
object to accomplish the same result: http://jsfiddle.net/3DS49/162/
For the minimal example you showed here, You could use angular.element
instead of $
or jQuery
. angular.element
is a subset but it supports .html().
However, it is still a bad practice, since the content could be put in scope
and the html content could be set as a direct binding in the button templates.
Edit: angular.element does not support selectors. You should use a native approach for that (getElementById and its friends). However, ensure you're not using jquery-specific selectors, like :first. E.g.:
var elements = document.querySelectorAll(".myclass");
//it is up to you to iterate over the elements and call angular.element(element).html(yourValueHere) on each element.
//NOTE: you can get document from $windowProvider.$get().document, or $window.document, depending on the place (you will use $window.document in this case).
Edit: check browser support here - there's no native angular way to support selectors.
For a practical implementation of the particular case, it is safe to see @ChrisFrank 's answer. However, remember you can wrap each of the elements iterated (e.g. in his answer) with angular.element
function to get a jqLite (AngularJS's built in subset of jQuery - you don't need any additional library) object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With