Load directives dynamically with ng-repeat

I am wrapping the HTML "select" element with my own directive. This directive should create a dropdown menu representing its options.

I am placing the select element with my custom directive / attribute arBootstrapSelect. This directive adds a dropdown menu with options inside it, repeated by ng-repeat.

The "option" elements in the "select" element are marked with the "arBootstrapSelectOption" sign. They must have a "content" attribute representing a dynamic directive. This dynamic directive must be compiled and shown in the dropdown menu.

Basically each option (labeled "arBootstrapSelectOption") compiles the "content" attribute with the $ compile service and injects it into a list that lives in the arBootstrapSelect directive. After that arBootstrapSelect should show the compiled parameters using ng-repeat. Hope it's not too difficult. I am getting Error Link

HTML:

<select ar-bootstrap-select class="form-control">
                <option ar-bootstrap-select-option value={{$index}} ng-repeat="c in countries" content="<ar-country country-id='{{$index}}'></ar-country>">


                </option>
            </select>


<div class="dropdown">
                <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                    Dropdown
                    <span class="caret"></span>
                </button>
                <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
                    <li ng-repeat="option in arBootstrapSelectOptions">
                        {{option[1]}}
                    </li>
                </ul>
            </div>

      

My directives:

(function () {
'use strict';
var app = angular.module('mainApp');

app.directive('arBootstrapSelect', function ($interval, $compile) {
    $scope.arBootstrapSelectOptions = [];

    function link(scope, element, attrs) {
        //element.hide();

    }

    return {
        restrict: 'A',
        link: link,
        scope: true
    }

});

app.directive('arBootstrapSelectOption', function ($compile) {
    function link(scope, element, attrs) {
        scope.arBootstrapSelectOptions.push([attrs.value, $compile(attrs.content)(scope)]);
    }

    return {
        scope: true,
        restrict: 'A',
        link: link,
    }

});
})();

      

This works, but it is ugly and slow:

(function () {
'use strict';
var app = angular.module('mainApp');

app.directive('arBootstrapSelect', function ($interval, $compile) {
    //$scope.arBootstrapSelectOptions = [];

    function link(scope, element, attrs) {
        //element.hide();

        scope.$on('arBootstrapSelectNewItem', function (event, data) {
            var test = $('<li></li>').append(data);
            element.parent().find('.dropdown-menu').append(test);

        });

    }

    return {
        restrict: 'A',
        link: link,
        scope: true,
        transclude: true
    }

});

app.directive('arBootstrapSelectOption', function ($compile) {
    function link(scope, element, attrs) {
        //scope.arBootstrapSelectOptions.push([attrs.value, $compile(attrs.content)(scope)]);
        scope.$emit('arBootstrapSelectNewItem', $compile(attrs.content)(scope));

    }

    return {
        scope: true,
        restrict: 'A',
        link: link
    }

});
})();

      

+3


source to share


1 answer


I don't quite understand your specific example, so I'll answer at a conceptual level.

It seems like, at a high level, all you want is to specify a template for ng-repeat

(for example <ar-country country-id='{{$index}}'>

) that is presented to the user, and put it in the template provided by the directive (for example, inside <li>

).

It would be easier and more convenient to provide the template as content rather than as an attribute. Then you only need to translate the content. (I avoid using it here <option>

as it is a directive in itself, and I don't quite understand what you are trying to do - I use instead <my-option>

):

<my-option ng-repeat="c in countries">
  <ar-country country-id='{{$index}}'></ar-country>
<my-option>

      

The directive will look like this:

.directive("myOption", function(){
  return {
    scope: true,
    transclude: true,
    template: '<li ng-transclude></li>'
  }
});

      

This will create conceptually the following HTML:



<my-option>
  <li><ar-country country-id="0"></ar-country></li>
</my-option>
<my-option>
  <li><ar-country country-id="1"></ar-country></li>
</my-option>
..

      

(The directive arCountry

will also be compiled and linked and can generate its own content)

Also, if the parent needs to register each child, it is better to use require

for linking rather than $emit

/ $on

. Thus, the parent

controller must be defined with a function for registering children. Again, if you need to provide additional template, translate the content:

<my-select>
  <my-option ng-repeat="c in countries>
    <ar-country country-id='{{$index}}'></ar-country>
  </my-option>
</my-select>

      

.directive("mySelect", function(){
  return {
    scope: true,
    transclude: true,
    template: '<h1>heading</h1><div ng-transclude></div>',
    controller: function(){
      // this can be called by the child
      this.registerChild = function(childElement, childController){
        // store the children, if needed
      }
    }
  }
});

      

To use this.registerChild

, we need to make myOption

use require

:

.directive("myOption", function(){
  return {
    scope: true,
    transclude: true,
    template: '<li ng-transclude></li>'
    require: ['myOption', '^mySelect'],
    controller: function(){
      // you can define some functions here
    },
    link: function(scope, element, attrs, ctrls){
      var me = ctrls[0], 
          selectCtrl = ctrls[1];

      selectCtrl.registerChild(element, me);
  }
});

      

0


source







All Articles