Неправильная пользовательская директива Collapsable завершается неудачно, когда оборачивается вокруг содержимого ng-repeat

0

Я написал настраиваемую управляющую директиву, которая обертывает существующие представления в контейнер, который допускает другую директиву, называемую кнопкой collapser, которая по существу пытается скрыть контейнер, предоставленный директивой сбрасывания.

разборный

myApp.directive('collapsable', [
    '$compile',
    function ($compile) {
        return {
            scope: {
                screenId: '@'
            },
            restrict: 'E',
            controller: [
                '$scope',
                function ($scope) {
                    var self = {};

                    self.GetContainer = function (innerHtml) {
                        console.log(innerHtml);
                        var html = "<div id='" + $scope.id + "'>" + innerHtml + "</div>";

                        return html;
                    };

                    self.DoCallBack = function (targetContainerId) {
                        var container = $('#' + targetContainerId);

                        if (container.length > 0) {
                            container.hide("slow");
                        }
                    }

                    // --- //

                    $scope.GetContainer = self.GetContainer;

                    $scope.reference = {
                        doCallBack: self.DoCallBack, // This is a function parameter, DONT use () !!!
                        screenId: $scope.screenId
                    }
                }
            ],
            link: function ($scope, $elem, $attrs) {
                var html = $scope.GetContainer($elem.html());

                var linkFn = $compile(html);
                var content = linkFn($scope);

                $elem.html(content);
            }
        }
    }
]);

CollapserButton

myApp.directive('collapserButton', [
    '$compile',
    function ($compile) {
        return {
            restrict: 'E',
            scope: {
                parent: '='
            },
            transclude: true,
            controller: [
                '$scope',
                function ($scope) {
                    var self = {};

                    self.HandleButtonClick = function () {
                        $scope.parent.doCallBack($scope.parent.screenId);
                    }

                    // --- SCOPE --- //

                    $scope.HandleButtonClick = self.HandleButtonClick;
                }],
            link: function($scope, $elem, $attrs) {
                var html = '<button class="btn btn-primary" ng-click="HandleButtonClick()">Collapse</button>';
                var linkFn = $compile(html);
                var content = linkFn($scope);
                $elem.html(content);
            }
        }
}]);

Применение

<collapsable screenid="screen1">
  ...
  ...
  ...
  <collapser-button parent="reference"/>
</collapsable>

Это работает в статической тестовой настройке, теперь я хочу применить ее к реальной работе:

У меня есть (одна страница), которая содержит следующее:

<div ng-controller="personController">
    <table>
        <thead>
            <tr>
                <th style="width: 33%" translate>Firstname</th>
                <th style="width: 33%" translate>Firstname2</th>
                <th style="width: 34%" translate>Name</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="person in people">
                <td>{{ person.Firstname }}</td>
                <td>{{ person.Firstname2 }}</td>
                <td>{{ person.Name }}</td>
            </tr>
        </tbody>
    </table>
</div>

Это само по себе прекрасно работает, однако, если я обернуваю вокруг него сворачиваемую директиву, то переводы и ng-повторитель больше не работают.

Возможно, вы заметили, что функция GetContainer для "collapsable" регистрирует innerHtml для консоли, для текущей настройки, которая возвращает эту информацию:

<div ng-controller="personController" class="ng-scope">
    <table>
        <thead>
            <tr>
                <th style="width: 33%" translate="" class="ng-scope">Firstname</th>
                <th style="width: 33%" translate="" class="ng-scope">Firstname2</th>
                <th style="width: 34%" translate="" class="ng-scope">Name</th>
            </tr>
        </thead>
        <tbody>
            <!-- ngRepeat: persoon in personen -->
        </tbody>
    </table>
</div>

Любые идеи о том, как объяснить/исправить это?

Обновить

Это становится очень странным, если я добавлю несуществующую функцию в мою сворачиваемую директивную ссылку, ng-repeat отображается правильно:

link: function($scope, $elem, $attrs) {
    var html = '<button class="btn btn-primary" ng-click="HandleButtonClick()">Collapse</button>';
    var linkFn = $compile(html);
    var content = linkFn($scope);

    FunctionThatDoesNotExist();

    $elem.html(content);
}

Как я уже сказал, он корректно отображен, но это не значит, что он исправлен, потому что родительская ссылка кнопки collapser не определена, хотя она правильно определена в родительской сворачиваемой директиве.

Я думаю, что действие $ compile не работает на 100%, т.е. не все Угловая логика активирована.

Plunkr

Plunkr

Если я поставлю первый комментарий Blah() (в script.js, строка 76) в комментарии, я получаю [[object HTMLDivElement]]:

link: function ($scope, $elem, $attrs) {
    var html = $scope.GetContainer($elem.html());

    var linkFn = $compile(html);
    var content = linkFn($scope);

    //Blah();

    $elem.html(content);
}
  • 0
    Можете ли вы предоставить образец plunkr?
  • 0
    Я посмотрю что я могу сделать.
Показать ещё 6 комментариев
Теги:
asp.net-mvc

1 ответ

1
Лучший ответ

У вас есть несколько ошибок, и в большинстве случаев с угловым вы не нуждаетесь в ручном манипулировании с DOM.
Итак, во-первых, вы используете неправильный метод html. angular.element - это элемент jqLite, который реализует несколько методов из jQuery, поэтому element.html аналогичен jQuery.html, так что, как вы можете видеть, вы можете передать ему только строку или функцию

.html( htmlString )
.html( function )

но вы пытаетесь передать элемент pass-html, и он неявно преобразуется в строку и показывает [[object HTMLDivElement]]

Вам также не нужно получать контент и вручную добавлять его, потому что встроенный transclude может сделать это автоматически, вы должны просто добавить шаблон.

Кроме того, вам не нужна ссылка collapserButton на родительскую, потому что вы можете получить необходимое значение от родительского контроллера напрямую.

Итак, ваши процедуры могут быть сведены к чему-то подобному

  angular.module('ngApp', [])
    .directive('collapsable', [
      '$compile',
      function($compile) {
        return {
          scope: {
            screenId: '@'
          },
          restrict: 'E',
          transclude: true,
          template: '<div id="{{screenId}}"><ng-transclude></ng-transclude></div>',
          controller: function($scope) {
            this.DoCallBack = function(targetContainerId) {
              var container = $('#' + targetContainerId);

              if (container.length > 0) {
                container.hide("slow");
              }
            }

            // --- //

            this.screenId = $scope.screenId;
          }
        }
      }
    ])
    .directive('collapserButton', [
      '$compile',
      function($compile) {
        return {
          restrict: 'E',
          require: '^?collapsable',
          template: '<button class="btn btn-primary" ng-click="HandleButtonClick()">Collapse</button>',
          link: function($scope, $elem, $attrs, $ctrl) {
            $scope.HandleButtonClick = function() {
              $ctrl.DoCallBack($ctrl.screenId);
            }

          }
        }
      }
    ])
    .controller('ngAppController', function($scope) {

      var self = {};

      self.people = [{
        "Firstname": "Jack",
        "Firstname2": "William",
        "Name": "Sparrow",
      }, {
        "Firstname": "Charles",
        "Firstname2": "Foster",
        "Name": "Kane",
      }, {
        "Firstname": "Hannibal",
        "Firstname2": "",
        "Name": "Lecter",
      }];

      // --- //

      $scope.people = self.people;

      $scope.a = 1;
      $scope.b = 2;
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<div ng-app="ngApp">
  <collapsable screen-id="screen1">
    <div ng-controller="ngAppController">
      <table style="width: 300px">
        <thead>
          <tr>
            <th style="width: 33%">Firstname</th>
            <th style="width: 33%">Firstname2</th>
            <th style="width: 34%">Name</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="person in people">
            <td>{{ person.Firstname }}</td>
            <td>{{ person.Firstname2 }}</td>
            <td>{{ person.Name }}</td>
          </tr>
        </tbody>
      </table>
    </div>
    <collapser-button parent-reference="reference"></collapser-button>
  </collapsable>
</div>

образец с ng-hide и без анимации

  angular.module('ngApp', [])
    .directive('collapsable', [
      '$compile',
      function($compile) {
        return {
          scope: {
            screenId: '@'
          },
          restrict: 'E',
          transclude: true,
          template: '<div id="{{screenId}}" ng-hide="hide"><ng-transclude></ng-transclude></div>',
          controller: function($scope) {
            this.DoCallBack = function(targetContainerId) {
                $scope.hide = true;              
            }

            // --- //

            this.screenId = $scope.screenId;
          }
        }
      }
    ])
    .directive('collapserButton', [
      '$compile',
      function($compile) {
        return {
          restrict: 'E',
          require: '^?collapsable',
          template: '<button class="btn btn-primary" ng-click="HandleButtonClick()">Collapse</button>',
          link: function($scope, $elem, $attrs, $ctrl) {
            $scope.HandleButtonClick = function() {
              $ctrl.DoCallBack($ctrl.screenId);
            }

          }
        }
      }
    ])
    .controller('ngAppController', function($scope) {

      var self = {};

      self.people = [{
        "Firstname": "Jack",
        "Firstname2": "William",
        "Name": "Sparrow",
      }, {
        "Firstname": "Charles",
        "Firstname2": "Foster",
        "Name": "Kane",
      }, {
        "Firstname": "Hannibal",
        "Firstname2": "",
        "Name": "Lecter",
      }];

      // --- //

      $scope.people = self.people;

      $scope.a = 1;
      $scope.b = 2;
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<div ng-app="ngApp">
  <collapsable screen-id="screen1">
    <div ng-controller="ngAppController">
      <table style="width: 300px">
        <thead>
          <tr>
            <th style="width: 33%">Firstname</th>
            <th style="width: 33%">Firstname2</th>
            <th style="width: 34%">Name</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="person in people">
            <td>{{ person.Firstname }}</td>
            <td>{{ person.Firstname2 }}</td>
            <td>{{ person.Name }}</td>
          </tr>
        </tbody>
      </table>
    </div>
    <collapser-button parent-reference="reference"></collapser-button>
  </collapsable>
</div>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню