开发带有动态模块集的 AngularJS 应用程序

我有一个具有复杂布局的应用程序,用户可以在其中放置(拖放)小部件(通过从预定义的100 + 小部件集中选择) ,其中每个小部件都是一个自定义实现,以特定的方式显示一组数据(使用 REST 调用获取)。我已经阅读了大量的博客文章、堆栈溢出问题和 AngularJS 官方文档,但是我不知道应该如何设计我的应用程序来处理这些需求。查看演示应用程序,有一个单独的模块(ng-app) ,当在。Js 文件的依赖模块被声明为它的依赖项,但是我有一个大的小部件集,不知何故,它是不明智的描述他们都在那里。我需要以下问题的建议:

  • 我应该如何设计我的应用程序和小部件-我应该有一个单独的 AngularJS 模块或每个小部件应该是一个指令的主模块?
  • 如果我将我的小部件设计为指令,是否有一种方法可以在指令中定义依赖关系。也就是说我的指令在执行中使用了 n- 日历?
  • 如果我将每个小部件设计为一个单独的模块,是否有办法将小部件模块作为依赖项动态添加到主模块?
  • 我应该如何设计控制器-每个小部件可能有一个控制器?
  • 如果视图中有来自同一类型的多个小部件,我应该如何分离状态(范围) ?
  • 有没有用 AngularJS 设计可重用小部件的最佳实践?

剪辑

有用的参考资料:

29052 次浏览

这些只是一般性的建议。

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

你说的是数百个小部件,把它们分成几个模块似乎很自然。有些小部件可能比其他小部件有更多的共同点。有些可能非常一般,适合其他项目,其他更具体。

如果我将我的小部件设计为指令,是否有一种方法可以在指令中定义依赖关系。也就是说我的指令在执行中使用了 n- 日历?

对其他模块的依赖是在模块级别上完成的,但是如果模块 A依赖于模块 B,而且 AB都依赖于模块 C,那么就没有问题。指令是以角度创建小部件的自然选择。如果一个指令依赖于另一个指令,您可以在同一个模块中定义它们,或者在模块级别上创建依赖项。

如果我将每个小部件设计为一个单独的模块,是否有办法将小部件模块作为依赖项动态添加到主模块?

我不知道你为什么要这么做我也不知道该怎么做。指令和服务在以角度使用之前不会初始化。如果你有一个庞大的指令库(小部件) ,并且知道你可能会使用其中的一些,但不是全部——但是你不知道当应用程序初始化时会使用哪些,你可以在你的模块加载之后“延迟加载”你的指令。我创建了一个示例 给你

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

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

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

如果视图中有来自同一类型的多个小部件,我应该如何分离状态(范围) ?

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

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

隔离作用域,将依赖关系保持在必要的最小值

Brian Ford 也写了一篇关于 用角度写一个巨大的应用程序的文章

这个问题对我也很重要。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 列表作为示例

<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 的主页只是使用一个完全独立的角度应用程序(ng-app)为每个例子/小部件。我想我也要这样做,除了改变 fetchCode 函数以获取 AJAX 的内容。