3

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?

Daniel Olszewski
  • 13,995
  • 6
  • 58
  • 65
  • 1
    It seems like the angular in the clone needs to be evaluated after being appended to the element. If you set a timeout (not a good solution but shows the issue) on multiselect() then the dynamic options show. I think the dynamic options won't generate until the transclude function (mainly the multiselect part) is complete or put on hold. – spaniol6 Nov 25 '15 at 15:15
  • Thanks, point well taken. I'm going to evaluate this further and hopefully find a desired solution. – Daniel Olszewski Nov 25 '15 at 15:22

2 Answers2

3

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

Yaron Schwimmer
  • 5,327
  • 5
  • 36
  • 59
  • This is probably a better way than trying to manually add in new ones. I can imagine some sort of conflict occurring when Angular tries to add an ambiguous element as a Select's child, as opposed to your way, which is very specific in its purpose. – Harris Nov 25 '15 at 14:57
  • The result is the same, dynamic options provided by the controller are still missing. There should be 5 options available in the select. – Daniel Olszewski Nov 25 '15 at 15:00
  • 5 options is what I see in my fiddle. do you have any errors in the console? – Yaron Schwimmer Nov 25 '15 at 15:02
  • @yarons in your fiddle on firefox I'm only seeing the static options – spaniol6 Nov 25 '15 at 15:11
  • Looks like there are differences between Chrome and Firefox. Chrome doesn't display the plugin at all, while FF shows it correctly, but dynamic options are missing. – Daniel Olszewski Nov 25 '15 at 15:11
  • Although it smells hacky and I don't really know why this works (any hint maybe?), it fixes the issue. Thank you very much. – Daniel Olszewski Nov 25 '15 at 17:23
0

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.

Community
  • 1
  • 1
Daniel Olszewski
  • 13,995
  • 6
  • 58
  • 65