将AngularJS与Liferay一起使用

时间:2014-01-15 14:50:21

标签: javascript angularjs liferay liferay-theme

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

  

为何选择AngularJS?        HTML非常适合声明静态文档,但是当我们尝试使用它来声明Web应用程序中的动态视图时,它就会动摇。   AngularJS允许您为应用程序扩展HTML词汇表。该   结果环境非常富有表现力,可读性和   快速发展。

我会通过开发Liferay-Theme和Portlets来简单地使用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名称一样。

这有什么帮助的呢?

在此我可以简单地访问Liferay JavaScript值和&amp;函数优于声明式html语法,没有直接的JavaScript函数调用,如AngularJS方式。

E.g。现在可以通过声明性的html代码获取Liferay JavaScript的值和函数,就像这里一样,用于获取Web内容显示中的当前URL:

Liferay current URL: {{Liferay.currentURL}}

enter image description here

但是,我的问题是:

  • 在Liferay中使用AngularJS global会发生哪些副作用?
  • 是否会出现性能问题?
  • 与其他JavaSripts冲突,例如合金?
  • 在portlet中使用AngularJS?

3 个答案:

答案 0 :(得分:19)

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

可能的副作用和冲突

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

如果你需要完整的jQuery而不仅仅是AngularJS的jqLit​​e实现,那么你必须确保在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。如果没有其他门户网站服务器的更改,您可能不会使用这些portlet,如果它们使用另一个ngApp名称或没有。优点是您可以在所有portlet中共享$rootScope,并且portlet可以以角度方式相互通信(例如$emit$on,共享服务,...)。

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

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

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

有用的提示 - 我希望

Portlet配置

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

在portlet的doView方法中,您可以创建一个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标记。但您也希望能够在角度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”。

我希望您能获得有关您的疑虑的有用信息。

答案 1 :(得分:1)

相当多的问题,让我尽量回答我的最新知识。

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

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

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

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

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

用不同的方式表达:关于表现,我担心,你必须自己衡量。

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

P,很多。对不起,这不是一个更黑的和白色的答案,你必须自己判断。希望这个推理有帮助

答案 2 :(得分:1)

希望这仍然有帮助; 我认为之前提到的评论已经过时了。可能没有名称间距问题以及性能可能会从实现变为实现。

关于在portlet中使用AngularJS或全局使用这里是我的输入:

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

现在,谈论全局使用AngularJS。我认为这是一种有趣的方式,我正在尝试实现这一点。这是完全不同的,因为,至少我的方法,将在AngularJS而不是在portlet上放置关于视图的所有内容。要在Liferay中使用它,我可以使用Delegator servlet,它可以让你使用Liferay的API。基本上,您可以为不同的类设置多个URL映射。我总是通过这些调用返回一个JSON对象。基本想法是这样的:      - Global AngularJS应用程序。      - 使用AngularJS的模板来创建整个视图。      - 使用Ajax调用委托人portlet映射到自定义类,该类将返回JSON对象。      - 使用AngularJS控制器从Delegator的调用中获取数据。

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