使用动态模块集开发AngularJS应用


81

我有一个具有复杂布局的应用程序,用户可以放置(拖放)小部件(通过从一组预定义的100多个小部件中进行选择),其中每个小部件都是显示数据集(使用REST调用获取)的自定义实现。以一种特定的方式。我已经阅读了大量的博客文章,stackoverflow问题和AngularJS官方文档,但是我不知道应该如何设计应用程序来满足那里的需求。查看演示应用程序,有一个模块(ng-app),当在.js文件中构建该模块时,相关模块被声明为其依赖项,但是我有大量的小部件,以某种方式建议不要全部描述它们那里。我需要以下问题的建议:

  • 我应该如何设计我的应用程序和小部件-我应该有一个单独的AngularJS模块还是每个小部件都应该是主模块的指令?
  • 如果我将小部件设计为指令,是否可以在指令中定义依赖关系。也就是说,我的指令在其实现中使用ng-calender?
  • 如果我将每个窗口小部件设计为单独的模块,是否可以动态添加窗口小部件模块作为对主模块的依赖关系?
  • 我应该如何设计控制器-每个小部件一个控制器?
  • 如果视图中有多个相同类型的小部件,应该如何区分状态(范围)?
  • 有使用AngularJS设计可重用小部件的最佳实践吗?

编辑

有用的参考资料:


1
我首先想到的是将每个小部件创建为一对单独的HTML / JS文件,并通过ng-include呈现。诀窍是仅加载必要的控制器JS文件。
2013年

我可以动态添加包含ng-include的html吗?
Adrian Mitev

1
我相信是这样。在主控制器中,您将具有一个集合,该集合定义了哪些小部件处于活动状态,在该集合的标记中,您将ng-include绑定到指向该小部件的视图HTML src的某些属性。我很确定这是可行的,但实际上我还没有做过这样的事情。
2013年

Answers:


61

这些只是一般性建议。

我应该如何设计我的应用程序和小部件-我应该有一个单独的AngularJS模块还是每个小部件都应该是主模块的指令?

您正在谈论数百个小部件,将它们拆分为几个模块似乎很自然。一些小部件可能比其他小部件具有更多共同点。有些可能非常笼统并适合其他项目,有些则更具体。

如果我将小部件设计为指令,是否可以在指令中定义依赖关系。也就是说,我的指令在其实现中使用ng-calender?

依赖于其他模块的模块级完成的,但如果模块没有问题A取决于模块B和两个AB取决于模块C。指令是在Angular中创建窗口小部件的自然选择。如果一个指令依赖于另一个指令,则可以在同一模块中定义它们,也可以在模块级别上创建依赖关系。

如果我将每个窗口小部件设计为单独的模块,是否可以动态添加窗口小部件模块作为对主模块的依赖关系?

我不确定您为什么要这样做,也不确定如何执行。在Angular中使用指令和服务之前,不会对其进行初始化。如果您有一个庞大的指令(小部件)库,并且知道您可能会使用其中的一些(但不是全部),但是您不知道在应用程序初始化时将使用哪些指令,您实际上可以“懒惰”加载模块后,加载”指令。我在这里创建了一个例子

这样做的好处是,即使有很多代码,您也可以使应用程序快速加载,因为您不需要在需要脚本之前就加载它们。缺点是第一次加载新指令时可能会有相当大的延迟。

我应该如何设计控制器-每个小部件一个控制器?

小部件可能需要其自己的控制器。控制器通常应该很小,如果它们很大,您可以考虑是否有任何功能可以更好地适合服务。

如果视图中有多个相同类型的小部件,应该如何区分状态(范围)?

毫无疑问,需要范围变量的小部件应该具有自己独立的范围(scope:{ ... }在指令配置中)。

有使用AngularJS设计可重用小部件的最佳实践吗?

隔离范围,将依赖项保持在必要的最低限度。观看Misko的有关Angular最佳做法的视频

布赖恩·福特(Brian Ford)也写了一篇文章,介绍如何在Angular中编写大型应用程序


感谢您的惊人回答!
Adrian Mitev

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);
      }
    };
  })

我将以“待办事项”列表为例。对于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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.