Динамическая генерация виджетов из данных json без потери производительности

Вот проблема, которую я пытаюсь решить: создание формы из переменного набора виджетов, где точные виджеты и их порядок определяются данными, а именно схемой. Первый подход, который я использовал, выглядит так (опуская ненужные детали):

контроллер.js:

 angular.module('app').controller(function($scope) {
  $scope.data = {
    actions: [{
      name: 'Action1',
      base: 'nova.create_server',
      baseInput: {
        flavorId: {
          title: 'Flavor Id',
          type: 'string'
        },
        imageId: {
          title: 'Image Id',
          type: 'string'
        }
      },
      input: [''],
      output: [{
          type: 'string',
          value: ''
        }, {
          type: 'dictionary',
          value: {
            key1: '',
            key2: ''
          }
        }, {
          type: 'list',
          value: ['', '']
        }]
    }]
  };

  $scope.schema = {
    action: [{
      name: 'name',
      type: 'string',
    }, {
      name: 'base',
      type: 'string',
    }, {
      name: 'baseInput',
      type: 'frozendict',
    }, {
      name: 'input',
      type: 'list',
    }, {
      name: 'output',
      type: 'varlist',
    }
    ]
  };
})

шаблон.html

      <div ng-controller="actionCtrl" ng-repeat="item in data.actions">
        <div ng-repeat="spec in schema.action" ng-class="{'right-column': $even && isAtomic(spec.type), 'left-column': $odd && isAtomic(spec.type)}">
           <typed-field></typed-field>
           <div class="clearfix" ng-show="$even"></div>
        </div>
      </collapsible-panel>

директивы.js

.directive('typedField', function($http, $templateCache, $compile) {
  return {
    restrict: 'E',
    scope: true,
    link: function(scope, element, attrs) {
      $http.get(
        '/static/mistral/js/angular-templates/fields/' + scope.spec.type + '.html',
        {cache: $templateCache}).success(function(templateContent) {
          element.replaceWith($compile(templateContent)(scope));
        });
    }
  }
})

Среди шаблонов, расположенных внутри '/fields/', простейшим шаблоном для поля строкового типа является

<div class="form-group">
  <label>{$ spec.title || makeTitle(spec.name) $}</label>
  <input type="text" class="form-control" ng-model="item[spec.name]">
</div>

Этот подход работает для - все виджеты визуализируются, привязки моделей работают, но как только я набираю одну букву внутри этих виджетов, область видимости изменяется, и виджеты перерисовываются, что приводит к: * потере фокуса * некоторой задержке по времени, что означает низкую производительность .

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

шаблон.html

      <div ng-controller="actionCtrl" ng-repeat="item in data.actions">
        <action></action>
      </div>

директивы.js

.directive('typedField', function($http, $templateCache, idGenerator, $compile) {
  return {
    restrict: 'E',
    scope: true,
    compile: function ($element, $attrs) {
      $http.get(
        '/static/mistral/js/angular-templates/fields/' + $attrs.type + '.html',
        {cache: $templateCache}).success(function (templateContent) {
          $element.replaceWith(templateContent);
        });
      return function (scope, element, attrs) {
        scope.title = $attrs.title;
        scope.type = $attrs.type;
        scope.name = $attrs.name;
      }
    }
  }
})

.directive('action', function($compile, schema, isAtomic) {
  return {
    restrict: 'E',
    compile: function(tElement, tAttrs) {
      angular.forEach(
        schema.action,
        function(spec, index) {
            var cls = '', elt;
            if ( isAtomic(spec.type) ) {
              cls = index % 2 ? 'class="right-column"' : 'class="left-column"';
            }
            elt = '<div ' + cls + '><typed-field type="' + spec.type + '" name="' + spec.name + '"></typed-field>';
            if ( index % 2 ) {
              elt += '<div class="clearfix"></div>';
            }
            elt += '</div>';
            tElement.append(elt);
        });
      return function(scope, element, attrs) {
      }
    }
  }
})

Вместо того, чтобы получать схему из области видимости, я предоставляю ее через внедрение зависимостей в фазу компиляции директивы (которая запускается только в первый раз — мне казалось, что это именно то, что мне нужно, чтобы избежать повторяющейся полной перерисовки виджетов). Но теперь вместо красиво выглядящих виджетов (как раньше) я получаю сырой html с привязками данных, которые вообще не оцениваются. Я предполагаю, что делаю что-то не так, но не понимаю, как мне правильно использовать функцию компиляции, чтобы избежать проблем с производительностью. Не могли бы вы дать совет, что следует исправить?


person Timur Sufiev    schedule 03.01.2015    source источник


Ответы (1)


Наконец я узнал, что должно быть возвращено из функции компиляции директивы в этом случае

  .directive('action', function($compile, schema, isAtomic) {
    return {
      restrict: 'E',
      compile: function(tElement, tAttrs) {
        angular.forEach(
          schema.action,
          function(spec, index) {
            var cls = '', elt;
            if ( isAtomic(spec.type) ) {
              cls = index % 2 ? 'class="right-column"' : 'class="left-column"';
            }
            elt = '<div ' + cls + '><typed-field type="' + spec.type + '" name="' + spec.name + '"></typed-field>';
            if ( index % 2 ) {
              elt += '<div class="clearfix"></div>';
            }
            elt += '</div>';
            tElement.append(elt);
         });
         var linkFns = [];
         tElement.children().each(function(tElem) {
           linkFns.push($compile(tElem));
         });
         return function(scope) {
           linkFns.forEach(function(linkFn) {
             linkFn(scope);
           });
         }
      }
   }
})

Что на самом деле делает $compile, так это вызывает функцию 'compile' каждой директивы, с которой он сталкивается, поэтому вызов $compile для шаблона текущей директивы приводит к бесконечной рекурсии, но вызов ее для каждого дочернего элемента этой директивы работал нормально.

person Timur Sufiev    schedule 08.01.2015