Разработка приложения AngularJS с динамическим набором модулей

У меня есть приложение со сложной компоновкой, где пользователь может добавлять (перетаскивать) виджеты (выбирая из предопределенного набора из 100 + виджетов), где каждый виджет представляет собой настраиваемую реализацию, которая отображает набор данных (выбирается с использованием REST) ​​определенным образом. Я прочитал множество сообщений в блогах, вопросов о стеке и официальных документах AngularJS, но я не могу понять, как мне придумать мое приложение для обработки там требований. Если посмотреть на демонстрационные приложения, есть один модуль (ng-app), а при его создании в файле .js зависимые модули объявляются как его зависимости, однако у меня есть большой набор виджетов, и почему-то не рекомендуется описывать их все там. Мне нужно предложение для следующих вопросов:

  • Как мне создать приложение и виджеты - должен ли я иметь отдельный модуль AngularJS, или каждый виджет должен быть директивой для основного модуля?
  • Если я создаю свой виджет в качестве директив, существует ли способ определить зависимость в директиве. То есть сказать, что моя директива использует ng-каландр в его реализации?
  • Если я создаю каждый виджет как отдельный модуль, есть ли способ динамически добавлять модуль виджета в зависимость от основного модуля?
  • Как мне спроектировать контроллеры - возможно, один контроллер на виджет?
  • Как я могу отделить состояние (область), если у меня есть несколько виджетов одного типа в представлении?
  • Существуют ли лучшие стратегии для разработки многоразовых виджетов с помощью AngularJS?

ИЗМЕНИТЬ

Полезные ссылки:

Ответ 1

Это просто общие советы.

Как мне создать свое приложение и виджеты - должен ли я иметь отдельный модуль AngularJS, или каждый виджет должен быть директивой для основного модуля?

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

Если я создаю свой виджет в качестве директив, есть ли способ определить зависимость в директиве. То есть сказать, что моя директива использует ng-каландр в его реализации?

Зависимости от других модулей выполняются на уровне модуля, но нет проблем, если модуль A зависит от модуля B, и оба A и B зависят от модуля C. Директивы - естественный выбор для создания виджетов в Angular. Если директива зависит от другой директивы, вы либо определяете ее в одном модуле, либо создаете зависимость от модульного уровня.

Если я проектирую каждый виджет как отдельный модуль, есть ли способ динамически добавлять модуль виджета в зависимость от основного модуля?

Я не уверен, почему вы захотите это сделать, и я не знаю, как это сделать. Директивы и службы не инициализируются до их использования в Angular. Если у вас есть огромная библиотека директив (виджетов) и знаете, что вы, вероятно, будете использовать некоторые из них, но не все из них, но вы не знаете, какие из них будут использоваться, когда приложение будет инициализировано, вы можете фактически "лениться" загрузите "свои директивы после загрузки вашего модуля. Я создал пример здесь

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

Как я должен проектировать контроллеры - возможно, один контроллер на виджет?

Виджет, вероятно, нуждается в собственном контроллере. Контроллеры обычно должны быть небольшими, если они становятся большими, вы можете подумать, есть ли какие-либо функциональные возможности, которые лучше подходят для службы.

Как я могу отделить состояние (область), если у меня есть несколько виджетов того же типа в представлении?

Виджеты, которым нужны переменные области видимости, без сомнения должны иметь свои собственные изолированные области (scope:{ ... } в конфигурации директивы).

Существуют ли лучшие стратегии для разработки многоразовых виджетов с помощью AngularJS?

Изолируйте область действия, сохраните зависимости до необходимого минимума. См. видео Misko о лучших практиках в Angular

Брайан Форд также написал статью о написании огромного приложения в Angular

Ответ 2

Этот вопрос также очень важен для меня. На главной странице AngularJS есть несколько примеров (вы могли бы назвать их виджетами), поэтому я пошел, хотя их исходный код попытался посмотреть, как они отделили свои виджеты.

Во-первых, они никогда не объявляют атрибут "ng-app". Они используют

function bootstrap() {
      if (window.prettyPrint && window.$ && $.fn.popover && angular.bootstrap &&
          hasModule('ngLocal.sk') && hasModule('ngLocal.us') && hasModule('homepage') && hasModule('ngResource')) {
            $(function(){
              angular.bootstrap(document, ['homepage', 'ngLocal.us']);
            });
      }
    }

чтобы убедиться, что все загружено правильно. Неплохая идея, но странно, что они нажимают на вас атрибут ng-app, поэтому даже не используют его сами. В любом случае здесь находится модуль начальной страницы, который они загружают с помощью приложения - http://angularjs.org/js/homepage.js

В директиве называется appRun

  .directive('appRun', function(fetchCode, $templateCache, $browser) {
    return {
      terminal: true,
      link: function(scope, element, attrs) {
        var modules = [];

        modules.push(function($provide, $locationProvider) {
          $provide.value('$templateCache', {
            get: function(key) {
              var value = $templateCache.get(key);
              if (value) {
                value = value.replace(/\#\//mg, '/');
              }
              return value;
            }
          });
          $provide.value('$anchorScroll', angular.noop);
          $provide.value('$browser', $browser);
          $locationProvider.html5Mode(true);
          $locationProvider.hashPrefix('!');
        });
        if (attrs.module) {
          modules.push(attrs.module);
        }

        element.html(fetchCode(attrs.appRun));
        element.bind('click', function(event) {
          if (event.target.attributes.getNamedItem('ng-click')) {
            event.preventDefault();
          }
        });
        angular.bootstrap(element, modules);
      }
    };
  })

В качестве примера я воспользуюсь списком ToDo. Для html они имеют

<div app-run="todo.html" class="well"></div>

а затем внизу страницы

<script type="text/ng-template" id="todo.html">
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">
    <span>{{remaining()}} of {{todos.length}} remaining</span>
    [ <a href="" ng-click="archive()">archive</a> ]
    <ul class="unstyled">
      <li ng-repeat="todo in todos">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText"  size="30"
             placeholder="add new todo here">
      <input class="btn-primary" type="submit" value="add">
    </form>
  </div>
</script>

У них также есть

<style type="text/css" id="todo.css"> //style stuff here </style>
<script id="todo.js"> //controller stuff here </script>

Используется код, но атрибуты id для этих сценариев не важны для запуска приложения. Это только для исходного кода, отображаемого слева от приложения.

В принципе, у них есть директива, называемая appRun, которая использует функцию fetchCode

  .factory('fetchCode', function(indent) {
    return function get(id, spaces) {
      return indent(angular.element(document.getElementById(id)).html(), spaces);
    }
  })

чтобы извлечь код. Затем они используют angular.bootstrap() для создания нового приложения. Они также могут загружать модули, хотя и запускать приложения. Пример проекта JavaScript инициализируется как

<div app-run="project.html" module="project" class="well"></div>

Надеюсь, это поможет. Я все еще не уверен, что такое "лучшая" техника, но похоже, что страница AngularJS просто использует совершенно отдельный angular приложение (ng-app) для каждого примера/виджета. Я думаю, что я собираюсь сделать то же самое, кроме изменения функции fetchCode, чтобы получить материал с AJAX.