7

我会将全局 AngularJS 与 Liferay Portal 一起使用。因为,就像 AngularJS 的设计一样:

为什么选择 AngularJS?HTML 非常适合声明静态文档,但是当我们尝试使用它来声明 Web 应用程序中的动态视图时,它会动摇。AngularJS 允许您为您的应用程序扩展 HTML 词汇。由此产生的环境极具表现力、可读性和快速开发。

我会通过开发 Liferay-Theme 和 Portlet 来简单地使用 html 的声明性语法。

对于这个要求,我创建了新的 Liferay-Theme 并定制了一点portal_normal.vm

<!DOCTYPE html>

<#include init />

<html ... ng-app="liferay">
<head>
        ...
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script>
    <script type="text/javascript" src="${javascript_folder}/my.js" charset="utf-8"></script>
</head>
<body class="${css_class}">
    <div ng-controller="LiferayCtrl">

这里my.js

angular.module('liferay', [])

.controller('LiferayCtrl', function($scope) {
    console.log("---==== Init AngularJS ====---");
    $scope.Liferay = Liferay;
});

我可以扩展控制器,比如获取 Liferay-Site 名称。

这一切有什么用?

因此,我可以通过声明性 html 语法简单地访问 Liferay JavaScript 值和函数,而无需像 AngularJS 那样直接调用 JavaScript 函数。

例如,现在可以通过声明性 html 代码获取 Liferay JavaScript 的值和函数,例如这里,用于在 Web 内容显示中获取当前 url:

Liferay current URL: {{Liferay.currentURL}}

在此处输入图像描述

但是,我的问题是:

  • 在 Liferay 中使用全局 AngularJS 会发生哪些副作用?
  • 它会出现性能问题吗?
  • 与其他 JavaSripts 冲突,例如 Alloy?
  • 在 portlet 中使用 AngularJS?
4

3 回答 3

19

首先:在门户网站上使用 AngularJS 是个好主意。到目前为止,我们只将 AngularJS 用于 portlet——我稍后会解释为什么以及如何使用。

可能的副作用和冲突

与其他 JavaScript 库或框架一样,使用 AngularJS 并没有更多的副作用。Liferay 随 AlloyUi ( http://alloyui.com/ ) 一起提供。这个 JavaScript 库基于yahoo 编写的YUI ( http://yuilibrary.com/ )。YUI 使用与 jQuery 不同的命名空间,因此您可以将 jQuery 与 AlloyUI 一起使用。如果你能够使用 jQuery 而没有任何副作用,你可能会更多地使用 AngularJS,因为 AngularJS 提供了 jQuery 的一个子集。

如果您不仅需要 AngularJS 的 jqLit​​e 实现,还需要完整的 jQuery,您只需确保在 AngularJS 之前包含 jQuery。这可以通过更改portal_normal.vm主题轻松完成:

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>$the_title - $company_name</title>
    <script type="text/javascript" src="$javascript_folder/jquery-2.0.3.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="$javascript_folder/angular-1.2.7.min.js" charset="utf-8"></script>
    $theme.include($top_head_include)
</head>

如果您在主题级别包含 JavaScript 文件,您可以为每个主题使用不同的版本。如果您有多个门户实例并且您没有机会在每个实例中使用相同的版本,这将非常方便。

表现

这取决于。ng-app必须知道,如果您将指令放在 html 元素上,Angular 将解析整个网页,如您的示例所示。如果您的页面非常大并且内容很多,则可能是性能问题。因此,最好将 ngApp 指令稍微放在页面中,并通过以下方式手动初始化 ngApp:

$().ready(function() {
    angular.bootstrap("css-selector", ['yourApp']);
});

在 portlet 中使用 AngularJS

有两种方法可以实现这一点。

1)在你的例子中的大 ngApp 上。你必须知道,你不能嵌套 ngApps。因此,每个 portlet 都必须是您的应用程序的一部分。使用这种方法,您将失去 portlet 可以为您的页面提供单独部分的可能性。所有 portlet 都需要知道您的 ngApp 的名称是什么,并且必须扩展这个 ngApp。如果它们使用另一个 ngApp 名称或不使用其他门户服务器,则您可能无法使用这些 portlet,而无需在其他门户服务器中进行更改。优点是您可以$rootScope在您的所有 portlet 中共享这些,并且这些 portlet 可以以有角度的方式相互通信(例如$emit$on共享服务,...)。

2) 每个portlet 都有自己的ngApp。在这种情况下,没有依赖关系。每个 portlet 都可以通过angular.bootstrap. 此外,每个 ngApp 仅针对网页的一小部分创建,并且仅在确实需要时创建。

在这两种方式中,您都应该避免使用路由。因为routeProvider每页只能使用一个。

我们决定使用第二种方法,以便 portlet 保持独立而不依赖于全局 ngApp。

有用的提示——我希望

Portlet 配置

您知道您可以配置您的 portlet。所以问题出现了:“我如何将我的 portlet 首选项提供给我的 angular portlet?” 当然,您可以发出 http 请求来获取它们。但这还不够,因为您拨打了不需要的电话。您的 portlet 通常是 java 服务器页面,因此最好在服务器上包含生成时的首选项并将这些信息提供给您的 Angular 应用程序。但是怎么做?

doView您的 portlet 的方法中,您可以创建一个 JSON 对象并将其存储在您的一个键下RenderRequest

public void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
    PortletPreferences prefs = renderRequest.getPreferences();

    Map<String, Object> values = new HashMap<String, Object>();
    values.put(key, value);

    renderRequest.setAttribute("config", new ObjectMapper().writeValueAsString(values));

    super.doView(renderRequest, renderResponse);
}

在您的 jsp 中,您可以访问此配置属性:

<div class="hidden" portlet-config>${config}</div>

portlet-config是解析 JSON 并将信息存储在服务实例中的指令:

.factory('portletConfigService', function(){ return {}})

.directive('portletConfig', ['portletConfigService', function(portletConfigService) {
    return {
        restrict: 'A',
        compile: function(elem, attrs) {
            var config = angular.fromJson(elem.text());
            angular.forEach(config, function(value, key){
                portletConfigService[key] = value;
            });
        }
    };
}])

现在您可以将 注入portletConfigService到每个控制器、服务、工厂或其他任何东西中,并使用 config 参数: portletConfigService.key.

语言属性

我们遇到的另一个问题是语言属性。通常你会在一个language.properties文件中定义它们并在你的jsp中使用它们。例如通过 fmt:message标签。但是您还希望能够在您的 Angular js 应用程序中访问它们。而你绝对不想要的是在你的来源中有两次。我们通过生成的服务和读取当前用户语言的属性并在 jsp 中创建角度内容的指令解决了这个问题:

<%@page contentType="text/javascript; charset=UTF-8" %>
<%@ page import="java.util.ResourceBundle" %>
<%@ page import="java.util.Locale" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="org.apache.commons.lang.StringEscapeUtils" %>
angular.module('translations', [])
.factory('translations', function(){ return {
<%
    ResourceBundle labels =  ResourceBundle.getBundle("language", request.getLocale());
    Enumeration<String> keys = labels.getKeys();
    while(keys.hasMoreElements()){
        String key = keys.nextElement();
        out.write("\""+key+"\":\""+StringEscapeUtils.escapeJavaScript(labels.getString(key))+"\"");
        if(keys.hasMoreElements()){
            out.write(",\n");
        }
    }
%>
}}) 

.filter('mpbtranslate', ['translations', function(translations) { 
    return function(input) { 
        var translation = translations[input];
        return translation? translation: '???'+input+'???'; 
    }; 
}]);

您现在可以将这些翻译用作服务和过滤器。例如<div>{{'title' | translate}}</div>,将在客户端评估为“Titel”。

我希望你能得到一些关于你的顾虑的有用信息。

于 2014-01-20T14:30:57.900 回答
1

问题范围很广,让我尽我所能回答。

拥有多个 JS 库(例如 AlloyUI 和 AngularJS)的副作用通常是:

  • 更高的加载时间
  • 可能的冲突

AlloyUI 在命名空间方面非常出色(由于底层 YUI 的工作),因此实际发生冲突的几率非常低。但是,没有人会向您保证永远不会发生冲突,您必须自己进行测试。(我相信在这种情况下你很安全)

当您询问性能问题时,您可能会要求更高的加载时间。我对此的反问是:您对什么性能感兴趣:

  • 首次访问时的加载时间低于秒(将受益于每页请求的文件更少)
  • 重复访问时的亚秒级加载时间(可以通过积极缓存 JS 和其他二级引用文件来缓解)
  • 页面完全显示之前的时间(例如,当页面完全加载后使用 JS 渲染页面时)
  • 开发时间/性能,很大程度上取决于您(团队)对所用技术的经验
  • 您的服务器可以处理的并发用户数(这可能会受到您向服务器发出的请求数量的影响。有时使用 Ajax 会降低服务器负载,有时会增加负载)
  • 浏览器内存消耗(可能是移动设备的问题)
  • 传输到浏览器的数据量(可能是移动设备或低带宽连接的问题,但可以通过积极缓存重复访问来缓解)

换一种说法:关于性能,恐怕你必须自己衡量。

我知道在您的 portlet 中使用 AngularJS 没有问题。尤其是当您的 portlet 可以依赖已加​​载的库(而不是自己加载)时,这应该是非常安全的。我能想到的一个缺点(但这对于所有 portlet 来说都是很自然的)是,如果所有 portlet 都依赖于使用所有可用库的相同版本,那么你最好。这也意味着在更新主题中包含的 AngularJS 版本时测试/升级所有 portlet。

呸,相当多。很抱歉,这不能是一个更黑白的答案,您必须自己判断。希望这个推理有帮助

于 2014-01-16T09:05:49.607 回答
1

希望这仍然有帮助;我认为前面提到的评论已经死了。可能没有名称间距问题,并且性能可能会因实现而异。

关于在 portlet 或全局范围内使用 AngularJS,这是我的输入:

我见过很多在 portlet 中使用 AngularJS 的例子。我已经尝试了一些基本示例,它确实利用了设置它的容易程度和自包含配置。

现在,谈论在全球范围内使用 AngularJS。我认为这是一种有趣的方式,我目前正在尝试实现这一点。这是完全不同的,因为至少我的方法将让有关视图的所有内容都放在 AngularJS 上而不是 portlet 上。为了将它与 Liferay 一起使用,我利用了 Delegator servlet,它允许您使用 Liferay 的 API。基本上,您可以设置多个 URL 映射到不同的类。我总是用这些调用返回一个 JSON 对象。基本思想是这样的: - 全球 AngularJS 应用程序。- 使用 AngularJS 的模板来创建整个视图。- 使用 Ajax 调用将代理 portlet 映射到自定义类,这将返回一个 JSON 对象。- 使用 AngularJS 控制器从 Delegator 的调用中获取数据。

我提到这有点不同,那是因为 Liferay 基本上只是一个服务提供者,整个渲染将留给 AngularJS。这似乎也更多地体现在 AngularJS 的本质上。

于 2015-03-03T18:55:27.307 回答