Goal
Refresh & Render the SPA component with updated model data when a user adds new content in dialog (the component does not have a sling model exporter, so product REFRESH with call .model.json returns 404)...
Other ways to solve this is...
1) by adding a filter server side, checking for incoming .model.json requests for specific components with no sling model exporter and return .json selector content...
2) solution 2 below, adds a generic sling model exporter for component resource types, fetching node content as JSON (same as .json selector output)
Demo | Package Install | Github
Solution
1) Updating the component is a 2 step process...
a) Trigger a custom event from the SPA editor with latest dialog data...
b) Listen to the event in component render script and update...
Trigger Custom Event
1) Add a client library to extend editable action EditableActions.REFRESH and pass the updated model in a custom event eaem-spa-component-refresh-event. Login to CRXDE Lite (http://localhost:4502/crx/de), create folder /apps/eaem-sites-spa-how-to-react/clientlibs
2) Create node /apps/eaem-sites-spa-how-to-react/clientlibs/clientlib-extensions of type cq:ClientLibraryFolder, add String[] property categories with value [cq.authoring.editor], String[] property dependencies with value lodash.
3) Create file (nt:file) /apps/eaem-sites-spa-how-to-react/clientlibs/clientlib-extensions/js.txt, add
refresh-component.js
4) Create file (nt:file) /apps/eaem-sites-spa-how-to-react/clientlibs/clientlib-extensions/refresh-component.js, add the following code
(function($, $document){
var EAEM_COMPONENTS = "eaem-sites-spa-how-to-react/",
EAEM_SPA_COMP_REFRESH_EVENT = "eaem-spa-component-refresh-event";
$document.on("cq-editables-loaded", overrideSPAImageCompRefresh);
function overrideSPAImageCompRefresh(){
var _origExec = Granite.author.edit.EditableActions.REFRESH.execute;
Granite.author.edit.EditableActions.REFRESH.execute = function(editable, config){
if(editable.type.startsWith(EAEM_COMPONENTS)){
$.ajax(editable.slingPath).done(function(compData){
sendComponentRefreshEvent(editable, compData);
});
}
return _origExec.call(this, editable, config);
};
}
function sendComponentRefreshEvent(editable, compData){
let event = new CustomEvent(EAEM_SPA_COMP_REFRESH_EVENT, {
detail: {
type: editable.type,
path: editable.path,
slingPath: editable.slingPath,
data: compData
}
});
window.dispatchEvent(event);
}
}(jQuery, jQuery(document)));
import { MapTo } from '@adobe/cq-react-editable-components';
import DOMPurify from 'dompurify';
import React, { Component } from 'react';
import {Link} from "react-router-dom";
const ImageEditConfig = {
emptyLabel: 'Image - Experience AEM',
isEmpty: function (props) {
return (!props || !props.fileReference || (props.fileReference.trim().length < 1));
}
};
class Image extends Component {
componentDidMount() {
//todo check for wcmmode
window.parent.addEventListener("eaem-spa-component-refresh-event", (event => {
if( !event.detail || (event.detail.type !== this.props.cqType)){
return;
}
Object.assign(this.props, event.detail.data);
this.forceUpdate();
}).bind(this));
}
get imageHTML() {
const imgStyles = {
"display": 'block',
"margin-left": 'auto',
"margin-right": 'auto'
};
return (
<div>
<Link to={this.props.imageLink}>
<img src={this.props.fileReference} style={imgStyles}/>
</Link>
</div>
);
}
render() {
return this.imageHTML;
}
}
export default MapTo('eaem-sites-spa-how-to-react/components/image')(Image,ImageEditConfig);
Solution 2
package com.eaem.core.models;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Image;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Map;
@Model(
adaptables = {SlingHttpServletRequest.class},
adapters = {ComponentExporter.class},
resourceType = {
"eaem-sites-spa-how-to-react/components/image",
"eaem-sites-spa-how-to-react/components/component-1",
"eaem-sites-spa-how-to-react/components/component-2"
}
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class EAEMGenericComponentSlingExporter implements ComponentExporter {
private static Logger log = LoggerFactory.getLogger(EAEMGenericComponentSlingExporter.class);
@Inject
private Resource resource;
@PostConstruct
protected void initModel() {
}
public ValueMap getEaemData(){
log.debug("EAEM Generic sling exporter handling - " + resource.getPath());
Map<String, Object> rhMap = new LinkedHashMap<String, Object>();
try{
rhMap = getComponentDataMap(resource);
addChildComponentDataMap(resource, rhMap);
}catch(Exception e){
log.error("Error getting component data in generic model exporter for resource - " + resource.getPath(), e);
}
return rhMap;
}
private void addChildComponentDataMap(Resource resource, Map<String, Object> rhMap){
Iterator<Resource> itr = resource.listChildren();
Resource child = null;
Map<String, Object> childMap;
while(itr.hasNext()){
child = itr.next();
childMap = getComponentDataMap(child);
rhMap.put(child.getName(), childMap);
addChildComponentDataMap(child, childMap);
}
}
private Map<String, Object> getComponentDataMap(Resource resource){
ValueMap vm = resource.getValueMap();
Map<String, Object> rhMap = new LinkedHashMap<String, Object>();
for(Map.Entry<String, Object> entry : vm.entrySet()) {
String key = entry.getKey();
if(key.startsWith("jcr:") || key.startsWith("sling")){
continue;
}
rhMap.put(key, entry.getValue());
}
return rhMap;
}
@Override
public String getExportedType() {
return resource.getResourceType();
}
}
package com.eaem.core.models;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
import org.json.JSONObject;
import org.osgi.service.component.annotations.Component;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import java.io.*;
import java.util.Iterator;
@Component(
service = Filter.class,
immediate = true,
name = "Experience AEM Default Sling Model Response Modifier Servlet Filter",
property = {
Constants.SERVICE_RANKING + ":Integer=-99",
"sling.filter.scope=COMPONENT",
"sling.filter.pattern=.*.model.json"
}
)
public class EAEMDefaultModelJSONFilter implements Filter {
private static Logger log = LoggerFactory.getLogger(EAEMDefaultModelJSONFilter.class);
public static String EAEM_DATA = "eaemData";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest)request;
String uri = slingRequest.getRequestURI();
if(!uri.endsWith(".model.json")){
chain.doFilter(request, response);
return;
}
SlingHttpServletResponse modelResponse = new DefaultSlingModelResponseWrapper((SlingHttpServletResponse)response);
chain.doFilter(slingRequest, modelResponse);
PrintWriter responseWriter = response.getWriter();
responseWriter.write(getModifiedContent(modelResponse.toString()));
}
private String getModifiedContent(String origContent){
String modifiedContent = origContent;
try{
JSONObject model = new JSONObject(origContent);
model = (JSONObject) replaceEaemDataObject(model);
modifiedContent = model.toString();
}catch(Exception e){
log.error("Error modifying model JSON content", e);
modifiedContent = origContent;
}
return modifiedContent;
}
private Object replaceEaemDataObject(JSONObject jsonObject) throws Exception{
Iterator<String> itr = jsonObject.keys();
String key;
JSONObject modJSONObj = new JSONObject();
Object jsonValue = null;
while(itr.hasNext()){
key = itr.next();
if(key.equals(EAEM_DATA)){
JSONObject eaemData = (JSONObject)jsonObject.get(EAEM_DATA);
eaemData.put(":type" , jsonObject.get(":type"));
return eaemData;
}else{
jsonValue = jsonObject.get(key);
if(JSONObject.class.isInstance(jsonValue)){
modJSONObj.put(key, replaceEaemDataObject((JSONObject)jsonValue));
}else{
modJSONObj.put(key, jsonValue);
}
}
}
return modJSONObj;
}
@Override
public void destroy() {
}
private class DefaultSlingModelResponseWrapper extends SlingHttpServletResponseWrapper {
private CharArrayWriter writer;
public DefaultSlingModelResponseWrapper (final SlingHttpServletResponse response) {
super(response);
writer = new CharArrayWriter();
}
public PrintWriter getWriter() throws IOException {
return new PrintWriter(writer);
}
public String toString() {
return writer.toString();
}
}
}



No comments:
Post a Comment