Как модулировать приложения/плагины AngularJS

У меня есть несколько вопросов о программной архитектуре относительно перехода от Grails (REST-API, части AngularJS, MongoDB, Tomcat, Spock, несколько плагинов) до Node.js + Angular.js. Я, вероятно, должен объяснить структуру кулака проекта Grails, поэтому здесь мы идем:

Существует основное приложение Grails (рядом с несколькими другими приложениями), которое построено на нескольких плагинах. Каждый из этих плагинов может сам запускаться, что означает, что он имеет собственный пользовательский интерфейс, отдельные шаблоны, службы, контроллеры, маршруты, тесты и т.д. Он также размещается в разных хранилищах. Это делают механизмы плагинов Grails. Преимущества - это меньше усилий по тестированию, меньше времени компиляции, модуляции, отдельных обязанностей и т.д.

Но все же время для компиляции и тестирования слишком дорого. Также мне не нравится тот факт, что API предоставляет части шаблонов/представлений. Я бы хотел, чтобы API-интерфейсы backend были "только для поддержки API-интерфейсов" и интерфейсом "просто для того, чтобы быть интерфейсом". Таким образом, каждое приложение/плагин AngularJS предоставит свой собственный вид, маршруты, сервис и т.д. Но они также могут зависеть от других плагинов.

Так что я хотел бы добиться, так это:

  • Одно из основных приложений AngularJS, которое включает в себя несколько плагинов (плагин может быть чем-то вроде генератора отчетов, гостевой книги или вообще, говоря о отдельной независимой части приложения, либо с определенным маршрутом, либо просто небольшой частью страницы).
  • Каждый плагин должен быть автономным приложением AngularJS (возможно, исполняемым во время разработки через grunt или так). Чтобы разработчику пользовательского интерфейса не нужно было запускать все бэкэнд-приложение, кроме того, что мы можем запускать функциональные тесты только с помощью JavaScript
  • Связь только через REST, интерфейс должен извлекать все данные из API
  • Каждый плагин должен быть проверен сам по себе
  • Плагин может потребовать работы других плагинов
  • Основной файл index.html(и app.js?) может быть предоставлен сервером Nginx, который отделен от остальной части бэкэнд (API)

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

В Grails механизмы плагинов каким-то образом объединяют зависящие от плагина настройки (такие как сопоставления URL-адресов, зависимости и т.д.) к основному приложению, в которое они включаются/вводятся, - это то, чего я хочу достичь и с помощью AngularJS. Итак:

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

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

Ответ 1

** Этот ответ неполный **

Я хочу убедиться, что я тебя понимаю, прежде чем я вникаю в это.

Здесь представлена ​​быстрая реализация модуля Loader для управления ленивой загрузкой (плагины, активы вендора, файлы администрирования и т.д.).

Помогает ли это?

angular.module('Bizcoin.loader')
.service('Loader', Loader);

function Loader($injector, $ocLazyLoad, User) {
  var Loader = {
    load: load,
    get: get,
    plugins: {},
    flags: {}
  };

  init();

  return Loader;

  function init() {
    load('vendors');
    if (userIsAdmin)
      load('admin');
  }

  function load(plugin) {
    Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js');
    return Loader.plugins[plugin].then(setFlag);

    function setFlag() {
      return Loader.flags[plugin] = true;
    }
  }

  function get(plugin) {
    return load(plugin).then(function() {
      return $injector.get(plugin);
    });
  }

}

Ответ 2

Я работаю над большим .Net/AngularJS-приложением, состоящим из 20 +, независимых, областей (или модулей) и некоторых основных функций, общих и повторно используемых во всех областях.

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

Каждая область действует как независимое приложение, которое зависит только от основной функциональности, всегда присутствует. Каждая область имеет свой собственный маршрут ASP.Net MVC. Каждая область регистрирует основное приложение, которое оно хочет предоставить. Когда клиент переходит на панель инструментов приложения, только основная часть приложения. Когда пользователь нажимает на ссылку в меню, он будет перемещаться по содержимому, предоставленному одной из областей, и загружается только ядро ​​плюс активы этого Района.

Давайте посмотрим, как это делается.

На главной странице приложения я загружаю скрипты следующим образом:

<script type="text/javascript">
    // a JS object with all the necessary app data from the server.
    // e.g.: menu data, etc
    window.appContext = @Html.Action("ApplicationContext", "Portal")); 
</script>

@Scripts.Render("~/bundles/angular-main")

@RenderSection("AngularAreas", required: false)

Я использую точечные узлы и секции .Net.

Основная (основная) часть приложения AngularJS состоит из конфигурации angular, служб интернационализации, службы глобальных уведомлений, компонентов многократно используемого интерфейса и т.д. Это загружается @Scripts.Render("~/bundles/angular-main").

Раздел @RenderSection("AngularAreas", required: false) будет заполняться каждой областью, когда пользователь перейдет к этой области.

Позволяет увидеть код AngularJS. Вот часть основного приложения.

// If user is visiting an Area, the NgModules array will be augmented.
// with the modules the Area wants to provide (to be bootstrapped)
export var LoadNgModules = [
    NgModules.Config,
    NgModules.Core
];

angular.module(NgModules.Bootstraper, LoadNgModules);

angular.element(document).ready(function () {
    angular.bootstrap(document, [NgModules.Bootstraper]);
});

Давайте рассмотрим пример Area now.

И вот как область будет поставлять свои активы, которые будут выводиться в @RenderSection("AngularAreas", required: false):

@section AngularAreas {
    @Scripts.Render("~/bundles/areas/staff-management")
}

Это простой набор, содержащий все скрипты для этого Района. Теперь рассмотрим важную часть кода AngularJS для этого Района.

var StaffManagementNgModule = 'areas.staff-management';

// Push our self into the modules to be bootstrapped
LoadNgModules.push(StaffManagementNgModule );

// Define the module
angular
    .module(StaffManagementNgModule , ['ngRoute', NgModules.Core])
    .config([
        '$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => {

            $routeProvider
                .when(staff', { template: '<staff></staff>' })
                .when('staff/details/:id', { template: '<staff-details></staff-details>' });
        }
    ]);;

Вот и все, отсюда Область является обычным приложением angular.

Подводя итог, мы загружаем основные (основные) функции AngularJS и предоставляем массив LoadNgModules, который область может заполнить своими собственными модулями.

мы загружаем скрипты Area и "our self" в массив LoadNgModules.

наконец, запустите angular.bootstrap.

Для полноты, вот фрагмент С#, показывающий, как область указывает основному приложению, что он доступен

public class ItemManagementModuleRegistration : IModuleRegistration
{
    public void Register(ModuleContext moduleContext)
    {
        string thisAreaName = "Staff";

        moduleContext.RegisterMenu(menuContext =>
        {
            var ItemsMenu = menuContext.Items(thisAreaName);

            // add urls and stuff...
        });

        // register more stuff with the moduleContext
    }
}

Используя отражение, можно легко найти, какие области "установлены".

Это основные движущиеся части установки. Каждый регион может иметь свой собственный API и тесты. Это довольно гибко.