This is an issue of timing - your selectpicker
directive is running before the <option>
elements have been created.
To get around this you need to delay your call to element.selectpicker()
until the <option>
elements have been created.
Response to comments:
@Lu4 #1: You're right, generally in Angular it's possible to avoid using $timeout
.
I've updated the answer to use $watch
instead (if you're using Angular 1.2+ then you'll probably want to use $watchCollection
).
@dluz #2: As far as I'm aware the $timeout
is reliable enough for this situation, however the updated answer should address any concerns about that.
Using document.ready()
isn't appropriate here because that relates to the browser's state of the loaded page, not how Angular works with the DOM. Because our Angular code is already running know that the ready event has already fired - document.ready()
has to fire before Angular can run.
The issue that Lu4 has encountered is to do with how Angular processes directives - it operates on each element from the outside in, initialising all directives before moving onto the child elements and their directives (for simplicity we're ignoring terminal
directives). So when the selectpicker
directive is invoked Angular hasn't processed the child elements yet.
Updated Answer:
Update your HTML to pass MyModels
into the selectpicker directive as the collection you want to watch.
<select multiple selectpicker="MyModels">
...
</select>
Then update the directive to set up a $watch
on the value of the selectpicker
attribute.
From my quick tests the $watch
will fire once the DOM has been updated and you should be able to call selectpicker()
.
myApp.directive('selectpicker', [
return {
function(){
restrict: 'A',
scope: true,
link: function(scope, element, iAttrs, controller){
console.log('selectpicker::link: element=', element.eq(0).children().length);
scope.$watchCollection(iAttrs['selectpicker'], function(newValue){
console.log('selectpicker::$watch: element=', element.eq(0).children().length);
element.selectpicker();
});
}
};
}]);
With this approach the $watch
will fire when the collection is set on the scope or if the collection is replaced. In Angular 1.2 you should use $watchCollection
so you can be notified if the number of items in the array change.
Orignal Answer:
To get around this you need to use the $timeout
service to delay your call to element.selectpicker()
until the next "tick".
myApp.directive('selectpicker', ['$timeout',
function($timeout){
return {
restrict: 'A',
link: function(scope, element, iAttrs, controller){
// Log the number of children of the <select>.
// This will be 0 because Angular hasn't processed the children yet.
console.log('selectpicker::link: element=', element.eq(0).children().length);
var initSelectpicker = function(){
// Now the children have been created.
console.log('selectpicker::init: element=', element.eq(0).children().length);
element.selectpicker();
}
$timeout(initSelectpicker, 0, false);
}
};
}]);
That should get things started but you're going to have to be careful - if MyModels
changes then you're going to have to make sure that the selectpicker is kept up to date as well.