aem-component-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AEM Component Development

AEM组件开发

AEM components are the building blocks of page content. Each component is a
cq:Component
node in the JCR that ties together:
  1. HTL markup — the rendering template (
    .html
    file)
  2. Sling Model — server-side business logic (Java)
  3. Dialog — author UI for content editing (
    cq:dialog
    )
  4. Client libraries — CSS/JS for frontend behavior
  5. Edit config — authoring toolbar behavior (
    cq:editConfig
    )
Prerequisite skill: This skill assumes HTL knowledge. Load the
htl-scripting
skill for expression syntax, block statements, XSS contexts, and global objects.
AEM组件是页面内容的构建块。每个组件都是JCR中的一个
cq:Component
节点,关联以下内容:
  1. HTL标记 — 渲染模板(
    .html
    文件)
  2. Sling Model — 服务器端业务逻辑(Java)
  3. 对话框 — 供作者编辑内容的UI(
    cq:dialog
  4. 客户端库 — 用于前端行为的CSS/JS
  5. 编辑配置 — 作者工具栏行为(
    cq:editConfig
前置技能: 本技能假定您已掌握HTL知识。如需了解表达式语法、块语句、XSS上下文和全局对象,请加载
htl-scripting
技能。

Component File Structure

组件文件结构

Standard Maven archetype layout (Touch UI, modern AEM):
ui.apps/src/main/content/jcr_root/apps/<project>/components/<component-name>/
├── .content.xml          ← cq:Component definition (title, group, supertype)
├── <component-name>.html ← HTL rendering script
├── _cq_dialog/
│   └── .content.xml      ← Touch UI dialog (Granite UI)
├── _cq_editConfig/
│   └── .content.xml      ← Edit behavior config
├── _cq_template/
│   └── .content.xml      ← Default content on drag-and-drop
├── _cq_design_dialog/
│   └── .content.xml      ← Policy/design dialog
└── clientlib/             ← Optional co-located clientlib
    ├── .content.xml
    ├── css.txt
    ├── js.txt
    ├── css/
    └── js/

core/src/main/java/<package>/models/
└── <ComponentName>Model.java  ← Sling Model
Note: Underscored directories (
_cq_dialog
) are the filesystem representation of JCR nodes with colons (
cq:dialog
). The FileVault serialization requires this naming.
标准Maven原型目录结构(Touch UI,现代AEM):
ui.apps/src/main/content/jcr_root/apps/<project>/components/<component-name>/
├── .content.xml          ← cq:Component定义(标题、分组、父类型)
├── <component-name>.html ← HTL渲染脚本
├── _cq_dialog/
│   └── .content.xml      ← Touch UI对话框(Granite UI)
├── _cq_editConfig/
│   └── .content.xml      ← 编辑行为配置
├── _cq_template/
│   └── .content.xml      ← 拖放时的默认内容
├── _cq_design_dialog/
│   └── .content.xml      ← 策略/设计对话框
└── clientlib/             ← 可选的本地客户端库
    ├── .content.xml
    ├── css.txt
    ├── js.txt
    ├── css/
    └── js/

core/src/main/java/<package>/models/
└── <ComponentName>Model.java  ← Sling Model
注意:带下划线的目录(如
_cq_dialog
)是带有冒号的JCR节点(如
cq:dialog
)在文件系统中的表示形式。FileVault序列化要求使用此命名方式。

Component Definition (
.content.xml
)

组件定义(
.content.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="My Component"
    jcr:description="A description for authors"
    componentGroup="My Project - Content"
    sling:resourceSuperType="core/wcm/components/text/v2/text"/>
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="My Component"
    jcr:description="A description for authors"
    componentGroup="My Project - Content"
    sling:resourceSuperType="core/wcm/components/text/v2/text"/>

Key Properties

关键属性

PropertyPurpose
jcr:title
Display name in component browser
jcr:description
Tooltip in component browser
componentGroup
Grouping in component browser. Use
.hidden
to hide
sling:resourceSuperType
Inherit from another component (rendering, dialog, edit config)
cq:isContainer
true
if component can contain child components (like a parsys)
cq:icon
Coral UI icon name for the component browser
abbreviation
2-char abbreviation if no icon
属性用途
jcr:title
组件浏览器中的显示名称
jcr:description
组件浏览器中的提示信息
componentGroup
组件浏览器中的分组。使用
.hidden
可隐藏组件
sling:resourceSuperType
继承另一个组件的功能(渲染、对话框、编辑配置)
cq:isContainer
如果组件可包含子组件(如parsys),则设为
true
cq:icon
组件浏览器中使用的Coral UI图标名称
abbreviation
若无图标,可设置2字符的缩写

Agent Workflow: Creating a New Component

代理工作流:创建新组件

Step 1: Define the component node

步骤1:定义组件节点

Create
.content.xml
with
jcr:primaryType="cq:Component"
, title, group, and optionally
sling:resourceSuperType
to extend an existing component.
创建
.content.xml
文件,指定
jcr:primaryType="cq:Component"
、标题、分组,可选设置
sling:resourceSuperType
以扩展现有组件。

Step 2: Create the Sling Model

步骤2:创建Sling Model

Write the Java model class in
core/
module. See references/sling-models.md.
java
@Model(adaptables = SlingHttpServletRequest.class,
       adapters = MyComponent.class,
       resourceType = "myproject/components/mycomponent",
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MyComponentImpl implements MyComponent {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    @Override
    public String getTitle() { return title; }

    @Override
    public String getDescription() { return description; }
}
core/
模块中编写Java模型类。详见references/sling-models.md
java
@Model(adaptables = SlingHttpServletRequest.class,
       adapters = MyComponent.class,
       resourceType = "myproject/components/mycomponent",
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MyComponentImpl implements MyComponent {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    @Override
    public String getTitle() { return title; }

    @Override
    public String getDescription() { return description; }
}

Step 3: Create the HTL script

步骤3:创建HTL脚本

html
<sly data-sly-use.model="com.myproject.core.models.MyComponent"/>
<div class="cmp-mycomponent"
     data-sly-test="${model.title || model.description}">
    <h2 class="cmp-mycomponent__title"
        data-sly-test="${model.title}">${model.title}</h2>
    <div class="cmp-mycomponent__description"
         data-sly-test="${model.description}">
        ${model.description @ context='html'}
    </div>
</div>
<sly data-sly-test="${!model.title && !model.description && (wcmmode.edit || wcmmode.preview)}">
    <div class="cq-placeholder" data-emptytext="My Component"></div>
</sly>
html
<sly data-sly-use.model="com.myproject.core.models.MyComponent"/>
<div class="cmp-mycomponent"
     data-sly-test="${model.title || model.description}">
    <h2 class="cmp-mycomponent__title"
        data-sly-test="${model.title}">${model.title}</h2>
    <div class="cmp-mycomponent__description"
         data-sly-test="${model.description}">
        ${model.description @ context='html'}
    </div>
</div>
<sly data-sly-test="${!model.title && !model.description && (wcmmode.edit || wcmmode.preview)}">
    <div class="cq-placeholder" data-emptytext="My Component"></div>
</sly>

Step 4: Create the dialog

步骤4:创建对话框

See references/dialogs.md for full Granite UI field reference.
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="My Component"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content jcr:primaryType="nt:unstructured"
             sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs jcr:primaryType="nt:unstructured"
                  sling:resourceType="granite/ui/components/coral/foundation/tabs"
                  maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties jcr:primaryType="nt:unstructured"
                                jcr:title="Properties"
                                sling:resourceType="granite/ui/components/coral/foundation/container"
                                margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns jcr:primaryType="nt:unstructured"
                                     sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                     margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <title jcr:primaryType="nt:unstructured"
                                                   sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                   fieldLabel="Title"
                                                   name="./title"/>
                                            <description jcr:primaryType="nt:unstructured"
                                                         sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                         fieldLabel="Description"
                                                         name="./description"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>
完整的Granite UI字段参考详见references/dialogs.md
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="My Component"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content jcr:primaryType="nt:unstructured"
             sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs jcr:primaryType="nt:unstructured"
                  sling:resourceType="granite/ui/components/coral/foundation/tabs"
                  maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties jcr:primaryType="nt:unstructured"
                                jcr:title="Properties"
                                sling:resourceType="granite/ui/components/coral/foundation/container"
                                margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns jcr:primaryType="nt:unstructured"
                                     sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                     margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <title jcr:primaryType="nt:unstructured"
                                                   sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                   fieldLabel="Title"
                                                   name="./title"/>
                                            <description jcr:primaryType="nt:unstructured"
                                                         sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                         fieldLabel="Description"
                                                         name="./description"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

Step 5: Add client library (if needed)

步骤5:添加客户端库(如需)

See references/clientlibs.md.
详见references/clientlibs.md

Step 6: Configure edit behavior (if needed)

步骤6:配置编辑行为(如需)

xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:EditConfig"
    cq:actions="[edit,delete,insert,copymove]"
    cq:layout="editbar"
    cq:dialogMode="floating">
    <cq:listeners jcr:primaryType="cq:EditListenersConfig"
                  afteredit="REFRESH_PAGE"
                  afterinsert="REFRESH_PAGE"/>
</jcr:root>
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:EditConfig"
    cq:actions="[edit,delete,insert,copymove]"
    cq:layout="editbar"
    cq:dialogMode="floating">
    <cq:listeners jcr:primaryType="cq:EditListenersConfig"
                  afteredit="REFRESH_PAGE"
                  afterinsert="REFRESH_PAGE"/>
</jcr:root>

Extending Existing Components

扩展现有组件

Use
sling:resourceSuperType
to inherit everything from a parent:
xml
<jcr:root ...
    jcr:primaryType="cq:Component"
    jcr:title="Custom Text"
    sling:resourceSuperType="core/wcm/components/text/v2/text"
    componentGroup="My Project"/>
What you inherit automatically:
  • HTL rendering scripts (resolved by Sling resource type hierarchy)
  • Dialogs (can overlay individual fields via Sling Resource Merger)
  • Edit config, descriptions, icons
To overlay a dialog field, create only the nodes you need to change under
_cq_dialog/
and set
sling:resourceSuperType
on the dialog root. The Sling Resource Merger merges your changes with the parent.
使用
sling:resourceSuperType
继承父组件的所有功能:
xml
<jcr:root ...
    jcr:primaryType="cq:Component"
    jcr:title="Custom Text"
    sling:resourceSuperType="core/wcm/components/text/v2/text"
    componentGroup="My Project"/>
自动继承的内容:
  • HTL渲染脚本(通过Sling资源类型层级解析)
  • 对话框(可通过Sling资源合并器覆盖单个字段)
  • 编辑配置、描述、图标
覆盖对话框字段,只需在
_cq_dialog/
下创建需要修改的节点,并在对话框根节点设置
sling:resourceSuperType
。Sling资源合并器会将您的修改与父组件的配置合并。

Core Components

核心组件

Always prefer extending AEM Core Components over building from scratch. They provide:
  • Production-tested, accessible, SEO-friendly markup
  • BEM CSS class naming (
    cmp-<name>__<element>--<modifier>
    )
  • Sling Model exporters for JSON/SPA
  • Style System support
  • Adobe Client Data Layer integration
Common Core Components to extend:
  • core/wcm/components/text/v2/text
  • core/wcm/components/image/v3/image
  • core/wcm/components/title/v3/title
  • core/wcm/components/teaser/v2/teaser
  • core/wcm/components/list/v4/list
  • core/wcm/components/container/v1/container
  • core/wcm/components/page/v3/page
优先扩展AEM核心组件而非从零开始构建。核心组件提供:
  • 经过生产验证、可访问、对SEO友好的标记
  • BEM CSS类命名规范(
    cmp-<name>__<element>--<modifier>
  • 用于JSON/SPA的Sling Model导出器
  • 样式系统支持
  • Adobe客户端数据层集成
常见的可扩展核心组件:
  • core/wcm/components/text/v2/text
  • core/wcm/components/image/v3/image
  • core/wcm/components/title/v3/title
  • core/wcm/components/teaser/v2/teaser
  • core/wcm/components/list/v4/list
  • core/wcm/components/container/v1/container
  • core/wcm/components/page/v3/page

Edit Placeholder Pattern

编辑占位符模式

Components MUST render something visible in edit mode even when empty:
html
<!--/* Reusable Core Components placeholder template */-->
<sly data-sly-use.template="core/wcm/components/commons/v1/templates.html"
     data-sly-call="${template.placeholder @ isEmpty=!model.hasContent}"/>
Or manual fallback:
html
<div class="cq-placeholder" data-emptytext="${component.properties.jcr:title}"
     data-sly-test="${(wcmmode.edit || wcmmode.preview) && isEmpty}"></div>
组件在编辑模式下即使为空,也必须渲染可见内容:
html
<!--/* 可复用的核心组件占位符模板 */-->
<sly data-sly-use.template="core/wcm/components/commons/v1/templates.html"
     data-sly-call="${template.placeholder @ isEmpty=!model.hasContent}"/>
或手动回退方案:
html
<div class="cq-placeholder" data-emptytext="${component.properties.jcr:title}"
     data-sly-test="${(wcmmode.edit || wcmmode.preview) && isEmpty}"></div>

Content Template (
_cq_template/
)

内容模板(
_cq_template/

Pre-populates the component's content node when dragged onto a page:
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="nt:unstructured"
    title="Default Title"
    description="Edit this component"/>
当组件被拖放到页面时,自动预填充组件的内容节点:
xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="nt:unstructured"
    title="Default Title"
    description="Edit this component"/>

Resource Type Resolution

资源类型解析

Sling resolves components by
sling:resourceType
on the content node:
  1. Content node has
    sling:resourceType = "myproject/components/hero"
  2. Sling looks for
    /apps/myproject/components/hero/hero.html
  3. If not found, checks
    sling:resourceSuperType
    chain
  4. Falls back to
    /libs/
    search path
Script resolution order for a resource type
myproject/components/hero
:
  1. hero.html
    (matches component name)
  2. html.html
    (matches extension)
  3. GET.html
    (matches method + extension)
  4. Then walks up the
    sling:resourceSuperType
    chain
Sling通过内容节点上的
sling:resourceType
解析组件:
  1. 内容节点的
    sling:resourceType = "myproject/components/hero"
  2. Sling查找
    /apps/myproject/components/hero/hero.html
  3. 若未找到,则检查
    sling:resourceSuperType
  4. 最后回退到
    /libs/
    搜索路径
资源类型
myproject/components/hero
的脚本解析顺序:
  1. hero.html
    (匹配组件名称)
  2. html.html
    (匹配扩展名)
  3. GET.html
    (匹配方法+扩展名)
  4. 然后遍历
    sling:resourceSuperType

Critical Rules

重要规则

  1. Always use Touch UI (
    cq:dialog
    ) — Classic UI dialogs are deprecated.
  2. Prefer Sling Models over
    WCMUsePojo
    — Sling Models are testable, cacheable, and the standard for AEM as a Cloud Service.
  3. Never modify
    /libs
    — always overlay in
    /apps
    . Use Sling Resource Merger for partial overrides.
  4. Always render an edit placeholder when component has no content.
  5. Use BEM naming for CSS classes:
    cmp-<component>__<element>--<modifier>
    .
  6. Set
    componentGroup
    or the component won't appear in the authoring UI.
  7. Store dialog field values with
    ./
    prefix in
    name
    attribute (e.g.,
    name="./title"
    ) to write relative to the component's content node.
  8. Extend Core Components before building from scratch.
  9. Client libraries under
    /apps
    need
    allowProxy=true
    to be served via
    /etc.clientlibs/
    .
  1. 始终使用Touch UI
    cq:dialog
    )—— 经典UI对话框已被弃用。
  2. 优先使用Sling Models而非
    WCMUsePojo
    —— Sling Models可测试、可缓存,是AEM as a Cloud Service的标准方案。
  3. 切勿修改
    /libs
    目录
    —— 始终在
    /apps
    目录中进行覆盖。使用Sling资源合并器进行部分覆盖。
  4. 组件为空时必须渲染编辑占位符
  5. CSS类使用BEM命名规范
    cmp-<component>__<element>--<modifier>
  6. 设置
    componentGroup
    —— 否则组件不会在作者UI中显示。
  7. 对话框字段值的存储—— 在
    name
    属性中使用
    ./
    前缀(如
    name="./title"
    ),以便将值写入组件的内容节点。
  8. 优先扩展核心组件而非从零开始构建。
  9. /apps
    下的客户端库
    需要设置
    allowProxy=true
    才能通过
    /etc.clientlibs/
    访问。

Reference Files

参考文档

  • references/dialogs.md — Touch UI dialog structure, Granite UI field types, tabs, validation
  • references/clientlibs.md — Client library folder setup, categories, dependencies, HTL inclusion
  • references/sling-models.md — Sling Model annotations, injection strategies, adapters, testing
  • references/dialogs.md — Touch UI对话框结构、Granite UI字段类型、标签页、验证
  • references/clientlibs.md — 客户端库文件夹设置、分类、依赖、HTL引入方式
  • references/sling-models.md — Sling Model注解、注入策略、适配器、测试