Daniel Olszewski
Daniel Olszewski

Reputation: 14411

Custom directive with ng-repeat as transcluded content

I'm working on a directive that wraps multiselect jQuery plugin. My goal is to convert the following HTML into a dynamic multiselect:

<select multiselect multiple ng-model="selected">
    <option>Static option 1</option>
    <option>Static option 2</option>
    <option ng-repeat="value in values">{{value}}</option>
</select>

Notice that options can be added directly or using ng-repeat to iterate over dynamic options.

Here's how I wrote the directive:

app.directive('multiselect', function () {
    return {
        restrict: 'A',
        scope: {
            model: '='
        },
        transclude: true,
        require: 'ngModel',
        link: function (scope, element, attrs, controller, transclude) {
            transclude(function (clone) {
                element.append(clone);
                $(element).multiselect();
            });
        }
    };
});

The problem is that while the directive works and replace HTML with the jQuery multiselect plugin, it displays only options provided statically. Options created using ng-repeat aren't displayed by the plugin, even though they can be seen in the HTML source rendered by Angular. It seems as if the transclude clone is appended to the element after multiselect is created by the plugin.

Here's the JSFiddle that reproduces this problem.

Is my understanding correct? What may be the cause of this behavior?

Upvotes: 3

Views: 174

Answers (2)

Daniel Olszewski
Daniel Olszewski

Reputation: 14411

For anyone who encounter the same problem, here's a complete link function that I used with successful results.

link: function (scope, element, attrs, controller, transclude) {
  transclude(function (clone) {
    element.append(clone);
  });
  $timeout(function() {
    element.multiselect();
  });
}

Don't forget to inject $timeout into the directive.

.directive('multiselect', function ($timeout) {

The transclude function takes care of appending content generated by the ng-repeat directive. The ng-options directive also works, however, in that case dynamic options are displayed before static ones. Only multiselect is wrapped using the $timeout.

I've also start digging in order to understand why using the timeout function actually helps and find some reasonable explanations in this answer and on this blog post by John Resig.

Upvotes: 0

Yaron Schwimmer
Yaron Schwimmer

Reputation: 5357

I'm not sure why your way doesn't work, but replacing option tag + ngRepeat with ngOptions seems to be doing the trick.

<select multiselect multiple ng-model="selected" ng-options="value for value in values">
  <option>Static option 1</option>
  <option>Static option 2</option>  
</select>

Working JSFiddle

Upvotes: 3

Related Questions