Author Yoko
这篇是一份完整的学习笔记,主要是对Youtube上的一套AEM的纯英文教程的练习梳理和爬坑,原教程视频你需要科学上网观看(你可以点击每章的标题跳转原视频地址),视频教程是印度人制作的,因此口音听起来十分奇怪,在学习过程中你可以结合我的笔记进行实战操作和演练。
这篇笔记适合小白入门,主要让你知道AEM的简单操作和使用,至于一些深入的概念和功能是什么,怎么用之类的,都需要看个人能力去研究,或参考我后续的文章(当然不会面面俱到)。如果你对AEM有比较深刻的认识和理解,建议直接跳过吧。
笔记较长,建议定位目录参考,如果你发现有问题,欢迎批评。
源码:Git仓库
工具和文档:pa空格n.ba空格idu.c空格om/s/1ZF_OUdw7y0OAVziJWbEFxg 提取码:yoko
文件重命名 可修改启动环境和端口
命令行启动
java -Xmx1024m -jar aem-author-4502.jar -gui
WebConsole
CRXDE Lite
Packages
Package Manger /crx/packmgr
Package Share
Https://www.adobeaemcloud.com/content/packageshare.html
以上链接都可以在Lite界面中的最上方左上角打开
/crx/packmgr
用来配合AEM开发,用于编辑页面,和IDEA类似
官网: http://brackets.io/
介绍: http://www.voidcn.com/article/p-qwozband-ho.html
安装完成后 file->extension manager 搜索AEM安装插件
将已打包出来的package项目解压,导入jcr_root到Brackets
选择AEM 设置好服务器地址
支持修改文件并导出到AEM Service
支持从AEM Service中将修改的文件导入
创建的目录结构
定义
创建
在structure中创建
在Component可创建html
<html>
<head>
head>
<body>
<h1>Hello World!!h1>
body>
html>
定义
Lite中创建
/content(/.*)?
Sites中使用template创建page
在Lite中
两个目录是关键
URL分解说明
自己新建一个page
http://localhost:4502/editor.html/content/demo02.html
在这个page的原型组件里添加一个blue.html
/apps/training/components/structure/mypage
添加blue.html<h1>This is a blue pageh1>
通过选择器访问新建的blue.html , 使用 .blue
http://localhost:4502/editor.html/content/demo02.blue.html
allowedPaths
allowedPaths /content(/.*)?
cq:allowedTemplates
Multi
按钮cq:allowedTemplates /templates目录 如:/apps/training/templates/.*
在site中依次创建如下的网页结构
Adobe官方学习文档-CN
以前称为 Sightly
Why?
HTL
注释
表达式
${}
Example: ${properties.jcr:title}
${!currentPage.hasChild}
${varOne || varTwo}
${varOne == varTwo}
Sly元素
Example:
=> <div data-sly-include="header.html">div>
Output:<div>header.htmldiv>
=> <sly data-sly-include="header.html">sly>
Output:header.html
Sightly Atrributes
Question: data-sly-use 和 data-sly-call 的区别?
call直接调用js里的方法
use先将js对象赋到某个自指定变量
data-sly-use 加载模板
data-sly-call 调用模板
参考文档
currentNode : It is an instance of the page(AEM api) class, which provides some methods to access content.
properties: It is an instance of the ValueMap(Sling api) class an contains all properties of the current resource.
currentNode : It is an instance of the Node(JCR api) class.
其实可以和JSP的9个内置对象相对照
代码见Chapter 7 Introduction to Sightly\Task - Render Basic Page Content
contentpage.html 不太长我直接粘过来了
<html>
<head>
<meta charset="utf-8"/>
head>
<body>
<h1>Hello World!!h1>
<h3>Sling PropertiesObjecth3>
<p>Page Title : ${properties.jcr:title}p>
<h3>Page Detailsh3>
<p>currentPage Title: ${currentPage.Title}p>
<p>currentPage Name: ${currentPage.Name}p>
<p>currentPage Path: ${currentPage.Path}p>
<p>currentPage Depth: ${currentPage.Depth}p>
<h3> Node Details h3>
<p>currentNode Name: ${currentNode.Name}p>
<p>currentNode Path: ${currentNode.Path}p>
<p>currentNode Depth: ${currentNode.Depth}p>
body>
html>
代码见Chapter 7 Introduction to Sightly\Task - Modularize Contentpage Component
创建header.html
<div class="navbar navbar-inverse navbar-fixed-top hidden-xs">
<div class="container-fluid">
<nav style="color: white;" >Language Navigationnav>
<ul class="nav navbar-nav navbar-right" style="color: white;" >
<sly>Toolbarsly>
ul>
div>
div>
<div >Site Navigationdiv>
创建footer.html
<footer class="we-Footer width-full">
<div class="container">
<div class="row">
<div class="row we-Footer-section we-Footer-section--sub">
<div class="we-Footer-section-item">
<div class="we-Logo we-Logo--big">
we.<strong>trainstrong>
div>
<div class="we-Footer-nav">
<h2>Footer Toolbarh2>
div>
div>
div>
<div class="row we-Footer-section we-Footer-section--sub">
i18n Coded Content
div>
<div class="row">
<div class="col-md-12">
<div class="text-center">
<a href="#top" class="btn btn-primary">Back to the topa>
div>
div>
div>
div>
div>
footer>
创建body.html
<div class="container we-Container--main">
<div class="root responsivegrid">
<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
<div class="header aem-GridColumn aem-GridColumn--default--12" data-sly-include="header.html">div>
<div class="hero-image image parbase aem-GridColumn aem-GridColumn--default--12" style="padding-top: 150px;">
<div>Hero Componentdiv>
<div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">
<div class="aem-GridColumn aem-GridColumn--default--12">
<div class="row">
<div class="aem-breadcrumb">Breadcrumbdiv>
<div class="we-Header">Title Componentdiv>
<div>Responsive Content Areadiv>
div>
div>
<form class="page__print">
<input value="Print Friendly" type="submit" />
form>
<div class="footer aem-GridColumn aem-GridColumn--default--12" data-sly-include="footer.html">div>
div>
div>
div>
div>
div>
修改contentpage.html
<html>
<head>
<title>${properties.jcr:title}title>
head>
<body>
<div data-sly-include="body.html">div>
body>
html>
打开页面查看效果
说明
AEM中的强大功能之一是从现有组件继承。 例如,AEM附带有一个Page组件,我们可以将该页面组件中已经预先构建的所有功能继承到我们自己的页面呈现组件中。
定义属性sling:resourceSuperType可以从超类型继承。
AEM在/ libs / wcm / foundation / components中提供了基础组件,在/ libs / foundation / components中提供了jsp组件。
如果在组件中没有找到脚本,则通过sling查找: sling:resourceSuperType属性,并在父类组件中找到默认脚本。
在Component中设置以下属性
sling:resourceSuperType String core/wcm/components/page/v2/page
如何调用ClientLibrary?
Css:
ALL:
有用的设置项
ClientLib一共分为4块讲解, 这是第一块
如何进入
如何进入WCM系统?
视频里的AEM版本较低, 在AEM6.5中WCM系统中已没有Design目录, 在Lite系统中/etc
下也没有Design目录了
新的目录在 /apps/settings/wcm/designs
网上一些人的建议
我的操作步骤
/apps/settings/wcm/designs
cq:page
,名称我写 training
training
中新建node, 类型是 nt:unstructured
, 名称是 jcr:content
代码见 Chapter 9 Design and Styles\Task - Define Design
导入代码中的package
将 clientlib (在 /apps/training/temp
) 移动到 /apps/settings/wcm/designs
使用 embed
属性来嵌入其他的clientlib
可参考 clientlib-all
和 clientlib-site
的属性
也可以自己创建clientlib目录, 类型是 cq:ClientLibraryFolder
, 注意属性 categories
必须Example:
clientlib-my
代码见 Chapter 9 Design and Styles\Task - Modify contentpage component to call the design
mypage.html
/apps/core/wcm/components/page/v2/page/head.html
引用在AEM6.5版本中, 不做上面这个Design 分配过程, 自定义的ClientLib也能被自动加载中
代码见Chapter 10 Authoring Structure Components\Lab Activity - I\Task 1 - Create a simple navigation component
Your App/components/content 目录下创建component, ep:
site-topnav
Top Navigation
拷贝代码 site-topnav.html
<nav class="navbar navbar-inverse navbar-absolute-top">
<ul class="nav navbar-nav navbar-center">
<li class="nav navbar-nav navbar-left" data-sly-repeat="${currentPage.listChildren}">
<a href="${item.path}.html">${item.title}a>
li>
ul>
nav>
修改 header.html , 导入navigation, 添加以下代码
<div class="navbar navbar-inverse navbar-fixed-top hidden-xs">
<div class="container-fluid">
<nav style="color: white;" >Language Navigationnav>
<ul class="nav navbar-nav navbar-right" style="color: white;" >
<sly>Toolbarsly>
ul>
div>
div>
<div data-sly-resource="${'site-topnav' @ resourceType='training/components/content/site-topnav'}">div>
在preview模式下点击查看效果
代码见 Chapter 10 Authoring Structure Components\Lab Activity - I\Task 2
拷贝代码 site-topnav.html
<div class="container we-Container--top-navbar">
<nav class="navbar navbar-inverse navbar-absolute-top">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#we-example-navbar-collapse-inverse" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span> <span class="icon-bar">span>
button>
<button type="button" class="navbar-toggle navbar-toggle-close collapsed" data-toggle="collapse" data-target="#we-example-navbar-collapse-inverse" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
button>
<a class="navbar-brand" href="#">we.<strong>trainstrong>a>
<div class="pull-right visible-xs">div>
div>
<div class="collapse navbar-collapse width" id="we-example-navbar-collapse-inverse">
<ul class="nav navbar-nav navbar-center">
<li class="visible-xs"><a href="#">we.<strong class="text-primary">trainstrong>a>li>
<li class="nav navbar-nav navbar-left" data-sly-repeat="${currentPage.listChildren}">
<a href="${item.path}.html">${item.title}a>
li>
<li class="visible-xs divider" role="separator">li>
ul>
div>
<span style="height: 0px;" class="navbar-shutter">span>
nav>
div>
测试
官网API文档
Steps
在 /apps/training/components/content/site-topnav
中创建 TopNav.java
, 复制代码, 路径如下
Chapter 10 Authoring Structure Components\Lab Activity - I\Task 3
package apps.training.components.content.site_topnav;
import java.util.*;
import java.util.Iterator;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.adobe.cq.sightly.WCMUsePojo;
public class TopNav extends WCMUsePojo{
private List<Page> items = new ArrayList<Page>();
private Page rootPage;
// Initializes the navigation
@Override
public void activate() throws Exception {
rootPage = getCurrentPage().getAbsoluteParent(2);
if (rootPage == null) {
rootPage = getCurrentPage();
}
Iterator<Page> childPages = rootPage.listChildren(new PageFilter(getRequest()));
while (childPages.hasNext()) {
items.add(childPages.next());
}
}
// Returns the navigation items
public List<Page> getItems() {
return items;
}
// Returns the navigation root
public Page getRoot() {
return rootPage;
}
}
拷贝 site-topnav.html
<div data-sly-use.topnav="TopNav" class="container we-Container--top-navbar">
<nav class="navbar navbar-inverse navbar-absolute-top">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#we-example-navbar-collapse-inverse" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span> <span class="icon-bar">span>
button>
<button type="button" class="navbar-toggle navbar-toggle-close collapsed" data-toggle="collapse" data-target="#we-example-navbar-collapse-inverse" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
button>
<a class="navbar-brand" href="${topnav.root.path}.html">we.<strong>trainstrong>a>
<div class="pull-right visible-xs">div>
div>
<div class="collapse navbar-collapse width" id="we-example-navbar-collapse-inverse">
<ul class="nav navbar-nav navbar-center">
<li class="visible-xs"><a href="${topnav.root.path}.html">we.<strong class="text-primary">trainstrong>a>li>
<li class="nav navbar-nav navbar-left" data-sly-repeat="${topnav.items}">
<a href="${item.path}.html">${item.title}a>
li>
<li class="visible-xs divider" role="separator">li>
ul>
div>
<span style="height: 0px;" class="navbar-shutter">span>
nav>
div>
注意API getAbsoluteParent(int level)
这里level定义了几就只能访问几级的子页面, 可以自行调试测试
/content/
表示0级JavaScript Use-API
一些可用公共变量
In addition to this, the Use-API has some common variables available right off the bat.
· currentNode
· currentPage
· resource
· log
· sling
· pageProperties
· properties
· pageManager
· component
· designer
· currentDesign
· currentStyle
Steps
在 /apps/training/components/content/site-topnav
中创建 topnav.js
, 复制代码, 路径如下
Chapter 10 Authoring Structure Components\Lab Activity - I\Task 4
// Server-side JavaScript for the topnav logic
use(function () {
var items = [];
var root = currentPage.getAbsoluteParent(2);
//make sure that we always have a valid set of returned items
//if navigation root is null, use the currentPage as the the navigation root
if(root == null){
root = currentPage;
}
var it = root.listChildren(new Packages.com.day.cq.wcm.api.PageFilter());
while (it.hasNext()) {
var page = it.next();
items.push(page);
}
return {
items: items,
root: root
};
});
修改 site-topnav.html
<div data-sly-use.topnav="topnav.js" class="container we-Container--top-navbar">
AEM Project Log路径
相对路径\crx-quickstart\logs
该路径下的log介绍
日志级别
使用Message记录日志信息:
Message is provided as parameter to the method call.
代码示例Eg: log.debug(“This is the log message”).
log设置控制台, 可以在控制台里自己添加log
Steps
修改 topnav.js
, 添加如下代码
//logging message
log.info("### Root page is : " + root.getTitle());
查看日志输出
Steps
修改TopNav.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(TopNav.class);
logger.info("Called in TopNav Java Helper, 成功使用java到处日志");
full code
package apps.training.components.content.site_topnav;
import java.util.*;
import java.util.Iterator;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.cq.sightly.WCMUsePojo;
public class TopNav extends WCMUsePojo{
private List<Page> items = new ArrayList<Page>();
private Page rootPage;
Logger logger = LoggerFactory.getLogger(TopNav.class);
// Initializes the navigation
@Override
public void activate() throws Exception {
rootPage = getCurrentPage().getAbsoluteParent(2);
if (rootPage == null) {
rootPage = getCurrentPage();
}
logger.info("Called in TopNav Java Helper, 成功使用java到处日志");
Iterator<Page> childPages = rootPage.listChildren(new PageFilter(getRequest()));
while (childPages.hasNext()) {
items.add(childPages.next());
}
}
// Returns the navigation items
public List<Page> getItems() {
return items;
}
// Returns the navigation root
public Page getRoot() {
return rootPage;
}
}
修改site-topnav.html, 修改use的内容
<div data-sly-use.topnav="TopNav" class="container we-Container--top-navbar">
测试
Different
可以根据 /libs/foundation/components/carousel
自己对比查看两个Dialog
查看更多信息 可搜索 aem widget api
代码见 Chapter 10 Authoring Structure Components\Lab Activity - III\Task 1 - Create Title Component
Steps
title
在 /apps/training/components/content/title
下
复制 cq:dialog
- /apps/core/wcm/components/title/v1/title/cq:dialog
./jcr:title
创建 cq:editConfig
- NodeType是 cq:EditConfig
打开页面测试
在content目录下找到保存的内容
路径参考, 根据实际变动的页面 /content/wetrain/en/aboutus/jcr:content/title
作用
Steps
cq:editConfig
下创建 cq:inplaceEditing
NodeType是 cq:InplaceEditingConfig
cq:inplaceEditing
添加两个属性
cq:inplaceEditing
下创建 config
NodeType默认AEM Tutorial English
https://helpx.adobe.com/experience-manager/using/touchUI_RTE_configure.html
Steps
找到 /libs/foundation/components/text/cq:dialog/content/items/text/items/column/items/text
拷贝 sling:resourceType
(如下)到/apps/training/components/content/title/cq:dialog/content/items/column/items/title
cq/gui/components/authoring/dialog/richtext
为title 节点添加属性(视频内容没有的)
useFixedInlineToolbar Boolean true
拷贝两个子Node
测试
PS
视频中用的sling:resourceType是
granite/ui/components/foundation/form/textfield
经试验无法使用
Steps
修改content/title组件的 title.html
, 显示富文本
<h1 data-sly-use.title="title.js">${title.text @context='html'}h1>
在 cq:dialog/content/items/column/items/title/rtePlugins
下添加Node findreplace
类型默认
添加属性 features String *
继续添加Node subsuperscript
添加属性 features String *
在 uiSettings/cui/inline
中为toolbar添加value
测试
Steps
在 cq:dialog/content/items/column/items/title/rtePlugins
下添加Node spellcheck
类型默认
添加属性 features String *
在 uiSettings/cui/inline
中为toolbar添加value
测试
What is design dialog?
两者不同之处
补充
/libs/foundation/components/title
下的dialog/apps/settings/wcm/designs
Steps
为 components/content/title
右键创建dialog
design_dialog
Design dialog
在 design_dialog/items/items/tab1
下创建 items
类型 cq:WidgetCollection
在下一级里面继续创建node type
类型 cq:Widget
添加以下属性
fieldLabel String Type
name String ./type
type String select
xtype String selection
defaultValue String h1
在node type
下继续创建 options
类型 cq:WidgetCollection
在 options
里面添加四个选项node
打开页面在Design模式下测试
Steps
数据保存在 /apps/settings/wcm/designs/training/jcr:content/mypage/title
修改 title.js
, 添加代码
title.type = currentStyle.get(CONST.PROP_TAG_TYPE) || "h1";
修改 title.html
<h1 data-sly-use.title="title.js" data-sly-element="${title.type}">${title.text @context='html'}h1>
Steps
创建component - hero
title - Hero Image
修改后缀, 拷贝代码
<div class="we-HeroImage width-full ratio-16by9" style="background-image:url(${properties.fileReference @context='styleString'})">
<div class="container cq-dd-image">
<div class="we-HeroImage-wrapper">
<strong class="we-HeroImage-title">${properties.jcr:title}strong>
div>
div>
div>
引入到pageComponent的body.html
<div data-sly-resource="${'hero' @ resourceType='training/components/content/hero'}">div>
拷贝image组件的cqdialog 路径 /libs/foundation/components/image/cq:dialog
删除无用的内容 留 file
和 title
为 hero
组件创建 cq:editConfig
打开页面测试 在edit
模式下设置图片, 并查看图片路径
通过创建EditConfig使得该组件可直接进行拖拽操作
Steps
为 hero
组件创建 cq:editConfig
在 cq:editConfig
下继续创建node cq:dropTargets
无类型
继续创建子node image
类型 cq:DropTargetConfig
添加属性
accept String image/*
groups String media
propertyName String ./fileReference
继续创建子node parameters
无类型
为 hero
组件添加以下属性
sling:resourceType String training/components/content/hero
打开页面测试拖拽图片
Steps(粗略版)
1.创建目录
2.创建区域语言的目录并设置属性 jcr:language
3.继续创建Node 类型 sling:MessageEntity
在Sites中(详细版)
创建German page
在structure目录/自己的页面组件下创建 i18n
目录
创建子文件夹
mix:language
在en目录下继续创建子node message
类型 sling:MessageEntry
sling:key String copy
sling:message String This is copyright statement
照上两步设置 fr & de , 注意在 sling:message
设置不同的语言
德语
Dies ist eine Urheberrechtserklärung
法语
Ceci est une déclaration de copyright
修改 footer.html
代码见 Chapter 12 Internationalization\Task 2 - Create content to be localized
<footer class="we-Footer width-full">
<div class="container">
<div class="row">
<div class="row we-Footer-section we-Footer-section--sub">
<div class="we-Footer-section-item">
<div class="we-Logo we-Logo--big">
we.<strong>trainstrong>
div>
<div class="we-Footer-nav">
<h2 data-sly-resource="${'toolbar' @ resourceType='foundation/components/toolbar'}">h2>
div>
div>
div>
<div class="row we-Footer-section we-Footer-section--sub">
<div class="we-Footer-section-item">
<span class="text-uppercase text-muted">${"copy" @ i18n, context='html'}span>
div>
<div class="we-Footer-section-item">
<a href="#" class="text-uppercase text-muted">Terms of use & privacy policya>
div>
div>
<div class="row">
<div class="col-md-12">
<div class="text-center">
<a href="#top" class="btn btn-primary">Back to the topa>
div>
div>
div>
div>
<sly data-sly-include="customfooterlibs.html" />
div>
footer>
代码关键是这句
<span class="text-uppercase text-muted">${"copy" @ i18n, context='html'}span>
打开不同的语言的page测试
以 hero
组件为例
创建 i18n
目录
创建子文件夹
mix:language
在en目录下继续创建
hero.image
类型 sling:MessageEntry
hero.title
类型 sling:MessageEntry
设置 fr 和 de
# de
Bild-Asset
Titel
# fr 就不设置了
在hero的ca:dialog中修改节点title&file的属性 fieldLabel
cq:dialog/content/items/column/items/file
的 fieldLabel
属性 为 hero.image
cq:dialog/content/items/column/items/title
的 fieldLabel
属性为 hero.title
介绍
/libs/wcm/foundation/components/responsivegrid
目录下parsys
相似, 并且能够放置组件Steps
到structure/自己的pageComponent目录
修改body.html
<div data-sly-resource="${'responsivegrid' @ resourceType='wcm/foundation/components/responsivegrid'}">div>
测试
就是开启不同设备的模拟环境
Steps - 失败了
在 apps/自己的project
下创建文件夹 config
打开 http://localhost:4502/system/console/configMgr , 搜索 mobileemul
复制 com.day.cq.wcm.mobile.core.impl.MobileEmulatorProvider
在 config 下创建node 类型 sling:OsgiConfig
名称: 类全限定~项目名
com.day.cq.wcm.mobile.core.impl.MobileEmulatorProvider~training
添加属性
mobile.resourceTypes String training/components/structure/mypage
为pageComponent (我这里是 mypage
) 添加属性
mobile.resourceTypes String training/components/structure/mypage
找到 /content/wetrain/jcr:content
添加属性
cq:deviceGroups String /etc/mobile/groups/responsive
Problem - Solved
前面过程在AEM6.5中失败了, 解决步骤:
在 apps/自己的project
下创建文件夹 config
打开 http://localhost:4502/system/console/configMgr , 搜索 mobileemul
找到 com.day.cq.wcm.mobile.core.impl.MobileEmulatorProvider~项目名
在里面添加组件路径
training/components/structure/mypage
点击save
PS: 这一步貌似不需要—为pageComponent (我这里是 mypage
) 添加属性
mobile.resourceTypes String training/components/structure/mypage
为需要的页面加入Emulator
找到 /content/wetrain/jcr:content
添加属性
cq:deviceGroups String /etc/mobile/groups/responsive
测试
在此模式下, 可以动态的修改组件的大小
可参考初始项目
Steps - fail
/content/we-retail/us/en/jcr:content/root/responsivegrid/teaser_305030210/cq:responsive
/content/training/jcr:content
Steps
修改 structure/mypage/body.html
<div class="aem-breadcrumb" data-sly-resource="${'breadcrump' @ resourceType='foundation/components/breadcrumb'}">Breadcrumbdiv>
测试
在Sites中的Navigation Bar中的按钮是可以通过Lite里设置改变的
现在我们修改以下 sites
按钮
Steps
定位到 /libs/cq/core/content/nav
找到sites
右键 overlay node 路径填 /apps/
勾选 match node type
刷新 定位到 /apps/cq/core/content/nav/sites
添加属性
jcr:title String WebSite
就会覆盖title的值
Overlay vs Sling Resource Meger
代码见 Chapter 13 Advanced Sling Functionality\Lab - Custom Error Handlers\Task 1 - Create a custom 404 error handler
自定义错误页面
/libs/sling/servlet/errorhandler
apps/
下AEM6.4创建命令
mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=13 -DarchetypeCatalog=https://repo.adobe.com/nexus/content/groups/public/
AEM6.5创建命令
mvn archetype:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=18
创建时的几个填写项说明
比较关键的
Archetype: Templating toolkit
Maven: Build tool
GroupId: Unique identifier
ArtifactId: Project name
setting.xml 设置以下adobe的配置, 防止下包失败
<profile>
<id>adobe-publicid>
<activation>
<activeByDefault>trueactiveByDefault>
activation>
<repositories>
<repository>
<id>adobeid>
<name>Nexus Proxy Repositoryname>
<url>https://repo.adobe.com/nexus/content/groups/public/url>
<layout>defaultlayout>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>adobeid>
<name>Nexus Proxy Repositoryname>
<url>https://repo.adobe.com/nexus/content/groups/public/url>
<layout>defaultlayout>
pluginRepository>
pluginRepositories>
profile>
使用maven命令编译部署
mvn clean install -PautoInstallPackage
Developer模式
项目结构
可以通过分析每个子模块中的 pom.xml
文件来分析每个结构对应的lite中的目录
以下视频作者原文
AEM project structure and importing project in Eclipse IDE:
parent pom: It deploys maven modules and manages dependency versions.
core : It includes java code and deploy to the AEM as osgi bundle.
ui.apps: It contains the /apps part of the project.
ui.content: It contains the /content and /conf part of the project.
ui.test: IT contains Junit tests that are executed server side.
ui.launcher: It contains code that deploys the ui.test bundle to the servers and triggers the remote Junit execution.
导入Eclipse
视频作者原文
Download the eclipse IDE from the link https://www.eclipse.org/downloads/pac… Once you download it, download the AEM developers tool from the link https://eclipse.adobe.com/aem/dev-tools/ Open the Eclipse and go to help - Install new software - add. Now give it title AEM developer too and put the above link and next. Select the modules and click and next and restart the Eclipse. Import the project. go to file - import - maven - existing project and click ok.
在Services窗口配置AEM服务器地址和端口, 选择好项目发布, 详细配置看视频
Eclipse支持的功能
使用了远程调试的功能
设置AEM启动监听Debug端口
方式一
找到AEMService目录 crx-quickstart\bin\start.bat
找到如下行添加JVM选项
::* default JVM options
dt_socket,server=y,suspond=n,address=自定义端口号
方式二
用以下命令启动AEM
java -jar aem-author-4502.jar -gui -debug 5005 #这里端口和idea保持一致
在IDE中使用远程调试, 注意端口号需要一致
什么是Content Fragment(后面简称CF) ?
Use Case
组成
使用模板创建CF
使用CF
Content Fragment
作为自定义CF的容器在CF编辑页面中 Navigation -> Assets -> Files -> 打开自己的CF
图片显示不出来的问题, 参考 官方文档
What is CF Model ?
Steps
Content Fragment Models
创建Folder
-> Properties -> Cloud Configuration创建一个Selector, 数据源动态读取
Dynamic dropdown with DataSource: In technical terms, data source is a factory to provide a collection of resource. Most of the times we use it to provide dynamic items to a container component.
Datasource within dialogs are used in place of the items node structure to represent the items of a container component.
You can mange the data source in a dialog and that’s using an acs commons list(generic list).
DOwnload the acs commons package from the link
https://adobe-consulting-services.github.io/acs-aem-commons/
After download, install the package and open the helloworld component in the demo training. Open the dialog box and create a node of dropdown (select) field. Follow the video for the rest of steps.
They are defined by a fragment template.
比较 Static Template 和 Editable Tenplate
在Editable Template中不同角色需要做的事
Editable Template的几种模式
Steps
进入Navigation -> Tools -> General -> Configuration Browser
点击Create 输入title ex. AEM-tutorial
, 勾选 Editable Templates
创建
创建好的模板目录对应在lite中的目录 /conf/上一步的文件夹名
注意里面的结构层次
进入Navigation -> Tools -> General -> Templates
进入前面自己的文件夹, 点击 Create , 选择模板创建 Template
使用Content Policies , 来限制每个Component对其他组件的使用
Example
Steps
先确保你创建的Editable Template已被启用
Navigation -> Tools -> General -> Templates -> 找到自己的ET, 选择右上角标点击 Enable
打开 Sites http://localhost:4502/sites.html/content
点击自己的Sites -> 选择Properties -> 选择Advanced
在下方找到 Templates Settings
, 写入自己刚才创建的 Editable Template 的路径(在lite里面找), 示例:
/conf/AEM-tutorial/settings/wcm/templates/.*
最末尾正则
保存, 就可以创建页面了
作用
Validation in AEM touch ui Dialog: Our dialogs are a collection of form fields and checking data before it’s submitted can save you lot of headache.
SO, input validation allows you to check the data before the dialog is submitted and Granit UI provides the easy way to add custom validation for your form fields.
Custom validation requires adding Custom JS and we can accomplish this using client library.
I will show above 2 methods in my next video and we will also see the difference between them.
在 content/组件
下创建 Node 类型 cq:ClientLibraryFolder
添加属性 categories String[] cq.authoring.dialog
最后如下目录结构
-clientlib-authoring
-js
*js.js
*js.txt
编写js, 阅读即可
$(document).on("foundation-contentloaded",function(e){
var container = e.target;
console.log(container);
})
原文
Adding JS via cq authoring dialog in AEM: AEM uses client library to manage js and css. Client libraries are registered globally in aem using a category name.
You give a category for it’s identification and then we call on the category to add js and css to the browser. AEM has a category specifically for dialogs and that is called cq.authoring.dialog. SO, we can create client library with the category cq.autoring.dialog to use any custom js within our dialog.
Important point to note is that adding js this way will add it to every dialog with an AEM, even the dialogs that come out of the box.
When granite ui injects new content into the DOM, I mean when granite ui load a dialog the Foundation-contentloaded event is triggered. When listening for this event, the container is available at the event targent. IT is recommended that the listener uses the container to scope it’s operation because event could be firing many times and you want to keep the javascript scoped to its purpose…
Create the client library with the category cq.authoring.dialog, add the js within it. In js, I have used cosole.log to print some msg on cl call and below we are listening for the event and when the event occurs, we will fire this anonymous function. Container is the event target. When this event is fired we should see some msg.
Steps
clientlibs的categories值
原文
Adding JS via IncludeClientlibs in AEM: There are 2 ways to include JS in our dialog
Create a clientlibrary and give the catergory any name e.g. cq.include and create js and js.txt file. Use the code in the video for js file.
Once client library is ready, open the dialog box and under the items node create a node and add properties sling:resourceType as granite/ui/components/coral/foundation/includeclientlibs and js as clientlib catergory name.
Now, open the page and in the console you will see the code loaded when you open the dialog of the specific component to which inlcudeclientlibs has been added.
说明
JS代码
$(window).adaptTo("foundation-registry").register("foundation.validation.validator", {
selector: "[data-should-contain]",
validate: function(el) {
var shouldContain = el.getAttribute("data-should-contain"); //aem
console.log('validating text contains aem');
console.log('input should contain ' + shouldContain);
var input = el.value; //input added by author
if (input.indexOf(shouldContain) === -1 ) {
return "The field should contain " + shouldContain + ". It's current value is " + el.value + ".";
}
}
});
Steps
在需要校验的node里面添加子节点
名称 granite:data
无类型
为granite:data 添加selector
test-selector
String test-need-value
参考js代码
$(window).adaptTo("foundation-registry").register("foundation.validation.validator", {
selector: "[data-test-selector]",
validate: function (el) {
let test = el.getAttribute("data-test-selector"); //test-need-value
let test2 = el.getAttribute("data-test-website");
let reg = /((http|https):\/\/)?(www)?.*?\.(com|cn|net|org|html)/;
let input = el.value;
console.log(input, test, test2);
if (reg.test(input)) {
return "The path must inside AEM, you can't set outer link!"
}
return;
//selector: "[is=coral-textfield]",
}
});
原文
Input field Validation using Granite UI in AEM: Now you know how to add custom js to your dialog. In this video we will write custom validation for input field using granite UI.
We can do it in aem using foundation-validation. Validation happens through a validator.
Previously, the jquery validator was used for valiadtion, but that is no longer the case as it has been deprecated but maintained for backward compatibility and new way for validation is foundation-validation approach.
To create a validator for form validation you need to register a new validator to the registry using foundation.validationvalidator name.
WHen you register a new validator you pass an object and object should contain a selector object and validate function object.
Copy the code from https://github.com/pankajchhatri/AEM and follow along for the validation.