Как я могу использовать интерполяцию для указания директив элемента? - PullRequest
0 голосов
/ 31 марта 2019

Я хочу создать представление в angular.js, в которое я добавляю динамический набор шаблонов, каждый из которых заключен в директиву.Имена директив соответствуют некоторому строковому свойству из набора объектов.Мне нужен способ добавить директивы, не зная заранее, какие из них понадобятся.

В этом проекте используется Angular 1.5 с веб-пакетом.

Вот версия в разобранном виде кода:

набор объектов:

$scope.items = [
    { name: "a", id: 1 },
    { name: "b", id: 2 }
]

директивы:

angular.module('myAmazingModule')
    .directive('aDetails', () => ({
        scope: false,
        restrict: 'E',
        controller: 'myRavishingController',
        template: require("./a.html")
    }))
    .directive('bDetails',() => ({
        scope: false,
        restrict: 'E',
        controller: 'myRavishingController',
        template: require("./b.html")
    }));

view:

<li ng-repeat="item in items">
    <div>
        <{{item.name}}-details/>
    </div>
</li>

, так что в конечном итоге визуализированный вид будет выглядеть так:

<li ng-repeat="item in items">
    <div>
        <a-details/>
    </div>
    <div>
        <b-details/>
    </div>
</li>

Как мне это сделать?

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

Ответы [ 3 ]

0 голосов
/ 01 апреля 2019

Вы можете добавить любой HTML с такими директивами:

const el = $compile(myHtmlWithDirectives)($scope);
$element.append(el);

Но обычно это не лучший способ, я просто дам немного более подробный ответ с использованием ng-include (который на самом деле вызывает $compile для вас):

Добавить шаблоны, например в module.run: [Вы также можете добавлять шаблоны в html, но когда они требуются в нескольких местах, я предпочитаю добавлять их напрямую]

app.module('myModule').run($templateCache => {
  $templateCache.put('tplA', '<a-details></a-details>'); // or webpack require
  $templateCache.put('tplB', '<b-details></b-details>');
  $templateCache.put('anotherTemplate', '<input ng-model="item.x">');
})

Ваша модель сейчас:

$scope.items = [
    { name: "a", template: 'tplA' },
    { name: "b", template: 'tplB' },
    { name: "c", template: 'anotherTemplate', x: 'editableField' }
]

И html:

<li ng-repeat="item in items">
   <div ng-include="item.template">
   </div>
</li>
0 голосов
/ 01 апреля 2019

Чтобы использовать динамические директивы , вы можете создать пользовательскую директиву, как я делал в этом plunkr:

https://plnkr.co/edit/n9c0ws?p=preview

Вот код нужной директивы:

app.directive('myProxy', function($compile) {
  return {
    template: '<div>Never Shown</div>',
    scope: {
      type: '=',
      arg1: '=',
      arg2: '='
    },
    replace: true,
    controllerAs: '$ctrl',
    link: function($scope, element, attrs, controller, transcludeFn) {
      var childScope = null;

      $scope.disable = () => {
        // remove the inside
        $scope.changeView('<div></div>');
      };

      $scope.changeView = function(html) {
        // if we already had instanciated a directive
        // then remove it, will trigger all $destroy of children
        // directives and remove
        // the $watch bindings
        if(childScope)
          childScope.$destroy();

        console.log(html);

        // create a new scope for the new directive
        childScope = $scope.$new();
        element.html(html);
        $compile(element.contents())(childScope);
      };

      $scope.disable();
    },
    // controller is called first
    controller: function($scope) {

      var refreshData = () => {
        this.arg1 = $scope.arg1;
        this.arg2 = $scope.arg2;
      };

      // if the target-directive type is changed, then we have to 
      // change the template
      $scope.$watch('type', function() {
        this.type = $scope.type;

        refreshData();

        var html = "<div " + this.type + " ";
        html += 'data-arg1="$ctrl.arg1" ';
        html += 'data-arg2="$ctrl.arg2"';
        html += "></div>";

        $scope.changeView(html);
      });

      // if one of the argument of the target-directive is changed, just change
      // the value of $ctrl.argX, they will be updated via $digest
      $scope.$watchGroup(['arg1', 'arg2'], function() {
        refreshData();
      });
    }
  };
});

Общая идея:

  • мы хотим, чтобы data-type мог указывать имя директивы для отображения
  • другие объявленные аргументы будут переданы целевым директивам.
  • во-первых, в ссылке мы объявляем функцию, способную создать подкаталог с помощью $ compile. 'link' вызывается после контроллера, поэтому в контроллере вы должны вызывать его асинхронно (в $ watch)
  • во вторых, в контроллере:
    • если type директивы изменяется, мы переписываем html, чтобы вызвать target-директиву
    • если обновляются другие аргументы, мы просто обновляем $ ctrl.argX, и angularjs запускает $ watch в дочерних элементах и ​​корректно обновляет представления.

Эта реализация в порядке, если все ваши целевые директивы имеют одинаковые аргументы. Я не пошел дальше.

Если вы хотите сделать его более динамичной, я думаю, вы могли бы установить scope: true и использовать атрибуты, чтобы найти аргументы для передачи директиве target.

Кроме того, вы должны использовать такие шаблоны, как https://www.npmjs.com/package/gulp-angular-templatecache, чтобы преобразовать ваши шаблоны в код, который вы можете объединить в своем приложении javascript. Это будет намного быстрее.

0 голосов
/ 31 марта 2019

Использование ng-include:

<li ng-repeat="item in items">
   <div ng-controller="myRavishingController"
        ng-include="'./'+item.name+'.html'">
   </div>
</li>

Я хочу встроить его, чтобы избежать HTTP-вызова.

Избегайте http-вызовов, загружая шаблоны напрямуюв шаблонный кеш одним из двух способов:

  • в теге сценария,
  • или непосредственным использованием службы $templateCache.

Длядополнительную информацию см.

...