Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

Custom Table Component

Avatar

Level 1

Hi, 

 

I'm trying to build a table component from scratch as the existing OOTB table is deprecated for  AEM 6.5. 

I wanted to include a component inside a table cell and should be able to edit it. For example, I want to include an image component in the last column of the table, I should be able to add it via dialog and edit it in page.

 

I got to know that it is not possible with the existing Core Text component table structure as RTE plugins would not allow adding a component inside a cell except text input. 

 

So, I tried to add custom dialog with multifield inputs and output the table as below.

 

<table width="100%" cellspacing="0" cellpadding="1" border="1">
<!--/* Get all child resources */-->
<div data-sly-list.rowsMultifield="${resource.getChildren}">
    <div data-sly-test="${rowsMultifield.name == 'rows'}">
        <ul data-sly-list.rows="${rowsMultifield.getChildren}">
                <tr>
                    <!--/* Get all child resources */-->
                    <div data-sly-list.cellMultifield="${rows.getChildren}">

                        <!--/* Keep only the countries multifield and iterate over its children */-->
                        <ul data-sly-test="${cellMultifield.name == 'cells'}"
                            data-sly-list.cell="${cellMultifield.getChildren}">


							<div data-sly-test="${properties.headerRow && (rowsList.index == 0)}">

                                <th>
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType='macnicagwi/components/content/text', decorationTagName='div', cssClassName='header-row'}">
                                    </sly>
                                </th>

							</div>

							<div data-sly-test="${properties.headerRow && (rowsList.index != 0) }">

                                <th data-sly-test="${properties.headerColumn && (cellList.index == 0)}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType='macnicagwi/components/content/text', decorationTagName='div', cssClassName='header-column'}">
                                    </sly>
                                </th>

                                <td data-sly-test="${properties.headerColumn && (cellList.index != 0)}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType=cell.type, decorationTagName='div'}">
                                    </sly>
                                </td>

								<td data-sly-test="${!properties.headerColumn}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType=cell.type, decorationTagName='div'}">
                                    </sly>
                            	</td>
							</div>



							<div data-sly-test="${!properties.headerRow}">

                                <th data-sly-test="${properties.headerColumn && (cellList.index == 0)}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType='macnicagwi/components/content/text', decorationTagName='div', cssClassName='header-column'}">
                                    </sly>
                                </th>
    
    
                                <td data-sly-test="${properties.headerColumn && (cellList.index != 0)}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType=cell.type, decorationTagName='div'}">
                                    </sly>
                                </td>
    
    
                                <td data-sly-test="${!properties.headerColumn}">
                                    <sly data-sly-resource="${ 'rows/{0}/cells/{1}/*' @ format=[rows.name,cell.name], resourceType=cell.type, decorationTagName='div'}">
                                    </sly>
                                </td>

							</div>
                        
                        </ul>
                    </div>
                </tr>
        </ul>
    </div>
</div>
</table>

And this is the dialog

vijay_karthik_0-1650462430127.png

So, the problem is everytime I add a new multifield and click on done, the entire table data is getting refactored to the beginning. All the edited data is getting lost. 

 

vijay_karthik_1-1650462548999.png

How do I retain the data in case if I add a new row or a cell?

OR, in short,  how to add a component in a table cell ?@

 

 

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Hi @vijay_karthik ,

 

Here is a tutorial to have a table component and also customising it in AEM :
https://www.youtube.com/watch?v=RRqovsPq5Lo

Please refer the same, let us know if you have any further concerns.

Thanks.

View solution in original post

6 Replies

Avatar

Correct answer by
Employee Advisor

Hi @vijay_karthik ,

 

Here is a tutorial to have a table component and also customising it in AEM :
https://www.youtube.com/watch?v=RRqovsPq5Lo

Please refer the same, let us know if you have any further concerns.

Thanks.

Avatar

Level 1
Level 1

<!-- /apps/your-project/components/custom-table/custom-table.html -->
<div class="custom-table">
<table>
<thead>
<tr>
<!-- Generate table headers -->
<sly data-sly-use.table="com.example.CustomTable">
<sly data-sly-repeat.header="${table.getHeaders()}">
<th>${header}</th>
</sly>
</sly>
</tr>
</thead>
<tbody>
<!-- Generate table rows and columns based on the provided data -->
<sly data-sly-use.table="com.example.CustomTable">
<sly data-sly-repeat.row="${table.getRows()}">
<tr>
<sly data-sly-repeat.col="${table.getColumns()}">
<td>Row ${row.index + 1}, Col ${col.index + 1}</td>
</sly>
</tr>
</sly>
</sly>
</tbody>
</table>
</div>

Avatar

Level 1
Level 1

custom-table
└── cq:dialog
├── content
│ └── items
│ └── root
│ ├── jcr:primaryType: nt:unstructured
│ ├── sling:resourceType: cq/gui/components/authoring/dialog
│ └── items
│ └── tab1
│ ├── jcr:primaryType: nt:unstructured
│ ├── sling:resourceType: cq/gui/components/authoring/dialog/tab
│ ├── title: Table Properties
│ └── items
│ └── columns
│ ├── jcr:primaryType: nt:unstructured
│ ├── sling:resourceType: cq/gui/components/authoring/dialog/column
│ ├── items
│ │ └── column
│ │ ├── jcr:primaryType: nt:unstructured
│ │ ├── sling:resourceType: cq/gui/components/authoring/dialog/fieldset
│ │ ├── items
│ │ │ ├── rows
│ │ │ │ ├── jcr:primaryType: nt:unstructured
│ │ │ │ ├── sling:resourceType: cq/gui/components/authoring/dialog/numberfield
│ │ │ │ ├── fieldLabel: Rows
│ │ │ │ ├── name: ./rows
│ │ │ │ ├── allowBlank: false
│ │ │ │ ├── minValue: 1
│ │ │ │ └── xtype: numberfield
│ │ │ ├── columns
│ │ │ │ ├── jcr:primaryType: nt:unstructured
│ │ │ │ ├── sling:resourceType: cq/gui/components/authoring/dialog/numberfield
│ │ │ │ ├── fieldLabel: Columns
│ │ │ │ ├── name: ./columns
│ │ │ │ ├── allowBlank: false
│ │ │ │ ├── minValue: 1
│ │ │ │ └── xtype: numberfield
│ │ │ ├── tableHeaders
│ │ │ │ ├── jcr:primaryType: nt:unstructured
│ │ │ │ ├── sling:resourceType: cq/gui/components/authoring/dialog/multifield
│ │ │ │ ├── fieldLabel: Table Headers
│ │ │ │ └── field
│ │ │ │ ├── jcr:primaryType: nt:unstructured
│ │ │ │ ├── sling:resourceType: cq/gui/components/authoring/dialog/textfield
│ │ │ │ ├── name: ./headers
│ │ │ │ └── allowBlank: false
│ │ └── jcr:primaryType: nt:unstructured
│ └── jcr:primaryType: nt:unstructured
└── jcr:primaryType: nt:unstructured


Java code:
======

package com.example;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import java.util.List;

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class CustomTable {

@Self
private Resource resource;

@ValueMapValue
private int rows;

@ValueMapValue
private int columns;

@ValueMapValue
private List<String> headers;

public int getRows() {
return rows;
}

public int getColumns() {
return columns;
}

public List<String> getHeaders() {
return headers;
}
}






Avatar

Level 1
Level 1

<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"
jcr:primaryType="cq:Dialog"
cq:dialogType="floating">
<content jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog">
<items jcr:primaryType="nt:unstructured">
<root jcr:primaryType="nt:unstructured">
<items jcr:primaryType="nt:unstructured">
<tab1 jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/tab" title="Table Properties">
<items jcr:primaryType="nt:unstructured">
<columns jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/column">
<items jcr:primaryType="nt:unstructured">
<column jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/fieldset">
<items jcr:primaryType="nt:unstructured">
<rows jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/numberfield"
fieldLabel="Rows"
name="./rows"
allowBlank="{Boolean}false"
minValue="{Long}1"
xtype="numberfield"/>
<columns jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/numberfield"
fieldLabel="Columns"
name="./columns"
allowBlank="{Boolean}false"
minValue="{Long}1"
xtype="numberfield"/>
<tableHeaders jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/multifield"
fieldLabel="Table Headers">
<field jcr:primaryType="nt:unstructured" sling:resourceType="cq/gui/components/authoring/dialog/textfield"
name="./headers"
allowBlank="{Boolean}false"/>
</tableHeaders>
</items>
</column>
</items>
</columns>
</items>
</tab1>
</items>
</root>
</items>
</content>
</jcr:root>

Avatar

Level 1
Level 1

<table>
<thead>
<tr class="header-content-row">
<td class="blank-cell"></td>
<td class="table-spacer-column"></td>
<sly data-sly-list.header="${model.addHeaders}">
<sly data-sly-test="${headerList.count < model.headercolumn}">
<th>
<div class="cell-container">
<div class="cell-container--content">
<div class="preline">
                               ${header.headerTitle @ context='html'}
</div>
<div class="headline"></div>
                           ${header.headerContent @ context='html'}
</div>
</div>
</th>
</sly>
</sly>
<td class="table-spacer-column"></td>
</tr>
</thead>
<tbody>
<sly data-sly-repeat.table="${model.addRows}">
<tr class="roundedTop-sm">
<th class="cell-header">
<div class="cell-header--content">
                   ${table.title @ context='html'}
</div>
</th>
<td class="table-spacer-column"></td>
<sly data-sly-list.table="${model.addRows}">
<sly data-sly-test="${tableList.count < model.columns}">
<td>
<div class="cell-container">
<div class="cell-container--content">
                               ${table.content @ context='html'}
</div>
</div>
</td>
</sly>
</sly>
<td class="table-spacer-column"></td>
</tr>
</sly>
</tbody>

</table>

Avatar

Level 1
Level 1

<table>
<thead>
<tr class="header-content-row">
<td class="blank-cell"></td>
<td class="table-spacer-column"></td>
<sly data-sly-list.header="${model.addHeaders}">
<sly data-sly-test="${headerList.count < model.headercolumn}">
<th>
<div class="cell-container">
<div class="cell-container--content">
<div class="preline">
                               ${header.headerTitle @ context='html'}
</div>
<div class="headline"></div>
                           ${header.headerContent @ context='html'}
</div>
</div>
</th>
</sly>
</sly>
<td class="table-spacer-column"></td>
</tr>
</thead>
<tbody>
<sly data-sly-repeat.row="${model.addRows}">
<tr class="roundedTop-sm">
<th class="cell-header">
<div class="cell-header--content">
                   ${row.title @ context='html'}
</div>
</th>
<td class="table-spacer-column"></td>
<sly data-sly-list.cell="${row.cells}">
<sly data-sly-test="${cellList.count < model.columns}">
<td>
<div class="cell-container">
<div class="cell-container--content">
                               ${cell.content @ context='html'}
</div>
</div>
</td>
</sly>
</sly>
<td class="table-spacer-column"></td>
</tr>
</sly>
</tbody>

</table>

<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"
jcr:primaryType="cq:Dialog"
cq:dialogMode="floating">

<content jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog">
<items jcr:primaryType="nt:unstructured">
<tabs jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/tabs">
<items jcr:primaryType="nt:unstructured">
<properties jcr:primaryType="nt:unstructured"
jcr:title="Properties"
sling:resourceType="cq/gui/components/authoring/dialog/tab">
<items jcr:primaryType="nt:unstructured">

<!-- Header Configuration -->
<headercolumn jcr:primaryType="nt:unstructured"
fieldLabel="Number of Header Columns"
name="./headercolumn"
sling:resourceType="cq/gui/components/authoring/dialog/numberfield"
allowBlank="{Boolean}false"
minValue="1"/>

<addHeaders jcr:primaryType="nt:unstructured"
fieldLabel="Table Headers"
name="./addHeaders"
sling:resourceType="cq/gui/components/authoring/dialog/multifield">
<field jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/compositefield">
<items jcr:primaryType="nt:unstructured">
<headerTitle jcr:primaryType="nt:unstructured"
fieldLabel="Header Title"
name="./headerTitle"
sling:resourceType="cq/gui/components/authoring/dialog/textfield"
allowBlank="{Boolean}false"/>
<headerContent jcr:primaryType="nt:unstructured"
fieldLabel="Header Content"
name="./headerContent"
sling:resourceType="cq/gui/components/authoring/dialog/textfield"
allowBlank="{Boolean}true"/>
</items>
</field>
</addHeaders>

<!-- Row Configuration -->
<columns jcr:primaryType="nt:unstructured"
fieldLabel="Number of Columns"
name="./columns"
sling:resourceType="cq/gui/components/authoring/dialog/numberfield"
allowBlank="{Boolean}false"
minValue="1"/>

<addRows jcr:primaryType="nt:unstructured"
fieldLabel="Table Rows"
name="./addRows"
sling:resourceType="cq/gui/components/authoring/dialog/multifield">
<field jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/compositefield">
<items jcr:primaryType="nt:unstructured">
<title jcr:primaryType="nt:unstructured"
fieldLabel="Row Title"
name="./title"
sling:resourceType="cq/gui/components/authoring/dialog/textfield"
allowBlank="{Boolean}false"/>
<cells jcr:primaryType="nt:unstructured"
fieldLabel="Cells"
name="./cells"
sling:resourceType="cq/gui/components/authoring/dialog/multifield">
<field jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/textfield"
name="./content"
fieldLabel="Cell Content"
allowBlank="{Boolean}false"/>
</cells>
</items>
</field>
</addRows>
</items>
</properties>
</items>
</tabs>
</items>
</content>
</jcr:root>