我的应用程序中有顶部菜单,我希望根据控制器有不同的内容。在 Rails 中使用 很容易content_for
,但是如何使用 angular 来实现呢?我已经知道这个解决方案: AngularJS:如何在控制器之间传递变量?但也许有更好的方法来做到这一点?
4 回答
Angular 的一个有趣之处在于,如果不了解应用程序的上下文,通常就没有“更好的方法”。有“其他”方式,最好的方式很大程度上取决于。我想更多信息也可能有助于定制或推荐特定答案。
然而,这是我关于这个话题的想法:
最初的想法
更明确的服务
您找到的答案还不错,尽管我可能会更进一步,并提供某种“菜单服务”而不是高度通用的“物业”商店。此菜单服务可以由ng-view
实例化的控制器操作。
通过路由映射
更进一步,可以在路由提供者声明中包含菜单信息,然后根据来自路由的数据$routeChangeSuccess
或$routeChangeStart
让菜单控制器更新自身(也许也维护服务,以便控制器可以贡献“特殊" 菜单选项,从而允许一定程度的定制)。
更多选择
如果共享服务(Angular 最佳实践,仅供参考)不符合您的喜好或设置,并且使用路由也不合适(可能很棘手),那么我可以看到更多选项:
$rootScope
一种是注入$rootScope
(所有范围的曾祖父)并在那里有一个集合,即您的菜单项;然后每个控制器都可以手动更新它。
自定义事件
这$rootScope.$emit()
是你的朋友——你可以发出某种事件并提供菜单配置数据。然后控制器将监听 ( $rootScope.$on()
) 事件并用新发出的菜单列表更新/清除/替换它自己的菜单项列表。
高级路由
更有趣的是,您甚至可以尝试看看在resolve
路线的一部分中包含函数是否可以解决问题。
参考
有关使用范围的信息在 Angular 的文档中:http://docs.angularjs.org/api/ng .$rootScope.Scope
有关复杂路由的信息在这里:http ://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#additional-features-of-controllers-and-routes(yearofmoo是 Rails 的粉丝,所以他们的意见可能与你的一致)
对于类似的任务,我编写了一个模仿 rails 的 angular js 指令content_for
对不起 CoffeeScript:
App.directive "contentFor", ["$sce", ($sce) ->
compile: (e, attrs, transclude) ->
pre: (scope, iElement, iAttrs, controller) ->
varname = iAttrs["contentFor"]
# Looks for the closest scope where 'varname' is defined
# or pick the top one
targetScope = scope
while targetScope.$parent
if ( if targetScope.hasOwnProperty then targetScope.hasOwnProperty(varname) else targetScope[varname] )
break
else
targetScope = targetScope.$parent
# Store html inside varname
html = iElement.html()
targetScope[varname] = $sce.trustAsHtml(html)
# remove the directive element
iElement.remove()
]
你可以像这样使用它
<span content-for="menuHtml">
<ul>
<li>Menu Item 1</li>
<li>Menu Item 2</li>
<li>Menu Item 3</li>
</ul>
</span>
这相当于<%= yield(:menuHtml) %>
:
<nav ng-bind-html="menuHtml"></nav>
最后,您可以将菜单重置为与$routeChangeSuccess
控制器中事件的默认绑定:
$scope.$on '$routeChangeSuccess', ->
$scope.menuHtml = $sce.trustAsHtml("<ul>...</ul>")
将您的顶级菜单绑定到顶级范围内的对象。在该控制器中创建一个方法来设置该变量。在您的子控制器中调用该方法来设置将根据您的控制器更改菜单的适当变量。
services
如果菜单项不是来自服务器,您可以利用它们来存储它们。
我认为直接引用将被替换的 div 而不是像 mcasimir 的答案那样使用 angular 的范围要简单得多。此外,它还有一个额外的好处,就是允许 angular 编译指令,在另一个答案中会给你一个“不受信任”的错误。这是指令的样子:
.directive('appContentFor', ($compile) -> {
replace: true,
compile: (element,attr,transclude)->
return (scope, element, attrs)->
element.remove()
node = $(attrs.node)
$(node[0]).replaceWith($compile(element.html())(scope))})
通过注入 $compile 服务,角度可以在 html 上完成它的工作,然后再将其插入它所属的位置。现在你可以像这样使用这个指令:
<div app-content-for node="div#awesomeContentFor">
<ul id="awesome_inserted_list">
<li>{{1+2}}</li>
<li>{{something_on_current_scope}}</li>
</ul>
</div>
然后在其他地方添加 id 为 awesomeContentFor 的 div:
<div id="awesomecontentfor"></div>