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

80

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

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

РЕДАКТИРОВАТЬ

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

Адриан Митев
источник
1
Моей первой мыслью было бы создать каждый виджет как пару отдельных файлов HTML / JS, визуализированных с помощью ng-include. Хитрость заключается в том, чтобы загрузить только необходимые JS-файлы контроллера.
Шмиддти
Я могу динамически добавлять html, содержащий ng-include?
Адриан Митев
1
Я так считаю. В вашем основном контроллере у вас будет коллекция, которая определяет, какие виджеты активны, в разметке для коллекции у вас будет ng-include, привязанный к некоторому свойству, которое указывает на указанное представление виджета HTML src. Я почти уверен, что это работает, но на самом деле я ничего подобного не делал.
Шмиддти

Ответы:

60

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Joakimbl
источник
Спасибо за прекрасный ответ!
Адриан Митев
17

Для меня этот вопрос тоже очень важен. На домашней странице 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.

бородатый
источник
3
+1 за это. У меня есть аналогичный вопрос, я думаю, вы могли бы помочь с: stackoverflow.com/questions/17557088/…
Дэн Канце
ты указал мне в правильном направлении. Для тех, кто интересуется функцией hasModule, я оставлю свою реализацию здесь:function hasModule(name) { try { angular.module(name); } catch(err) { return false; } return true; }
Asier Paz