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