2014-10-31 106 views
0

我是CQ的新手。我想要做的是能够从服务或servlet中检索的数据填充页面上的一个现成的Dropdown List组件。从JSON数据填充页面上的下拉列表(不在对话框中)

我见过从servlet填充编辑对话框上的字段的解决方案,但不是在页面本身。我知道我可以手动将每个单独的行添加到下拉列表的编辑面板中的下拉菜单中。我也看到了项目加载路径,我可以提供一个类型为String []的节点属性的路径,其中属性的每个元素都是格式化的key = value。这两种解决方案都是非常手动的,并且对于需要具有动态性的列表没有灵活性。

我知道我也可以定义一个servlet路径并通过ajax调用手动加载下拉列表......但这些数据是足够静态的,为了提高效率,可以/应该在构建页面时取回另一次往返服务器。在我看来,应该有办法将下拉列表绑定到一些JSON数据,这些数据在servlet或服务中动态构建,并在构建页面时填充到下拉列表中。也许可以将Items Load Path指向资源类型与servlet绑定的节点?

我刚刚对CQ很陌生,以至于我很难弄清楚如何将这些东西结合在一起,但似乎这是一个相当普遍的需求,即使用我没有的数据填充下拉菜单手动硬编码到页面或节点中。

回答

0

我不知道当我写这些时,我已经把我的问题写得很好。我从那时开始学到了一些东西,所以我想我想说的是我想要有一种方法来生成在JCR中没有真正支持的“合成”节点,而是动态构建的。我最终最终决定如何做的是写一个ResourceProvider。我的具体使用情况,这是提供一个简单的方法为作者来填充通过从两个地方起源数据下拉组件:

  • 打包属性文件
  • 呼叫转移至外线REST资源

我的灵感来自我的解决方案,主要来自于这篇文章:http://www.lucamasini.net/Home/sling-and-cq5/accessing-relational-data-as-sling-restful-urls

这是我写的大部分类。我忽略了从属性文件和REST资源读取的逻辑,因为这不是问题的关键。

@Component(
    name = "DropdownResourceProvider", 
    label = "DropdownResourceProvider", 
    description = "Dropdown Resource Provider") 
@Service 
@Properties({ 
    @Property(name = "service.description", value = "Dropdown Resource Provider"), 
    @Property(name = ResourceProvider.ROOTS, value = "/content/<app-name>/dropdown") 
}) 
public class DropdownResourceProvider implements ResourceProvider { 
    private static final Logger log = LoggerFactory.getLogger(DropdownResourceProvider.class); 

    private String providerRoot; 
    private String providerRootPrefix; 

    protected void activate(BundleContext bundleContext, Map<?, ?> props) { 
     providerRoot = props.get(ROOTS).toString(); 
     providerRootPrefix = providerRoot.concat("/"); 
    } 

    @Override 
    public Resource getResource(ResourceResolver resourceResolver, 
      HttpServletRequest httpServletRequest, String path) { 
     return getResource(resourceResolver, path); 
    } 

    @Override 
    public Resource getResource(ResourceResolver resourceResolver, final String path) { 
     if (providerRoot.equals(path) || providerRootPrefix.equals(path)) { 
      log.info("path " + path + " matches this provider root folder: " 
        + providerRoot); 
      return new SyntheticResource(resourceResolver, path, "sling:Folder"); 
     } else { 
      String relativePath = path.substring(providerRootPrefix.length()); 

      final String[] pathSegments = relativePath.split("/"); 

      if (pathSegments.length > 0) { 
       String[] dropdownOptions; // will be a string array formatted like this: ["key=value","key=value"] 

       if (REST_SEGMENT_NAME.equalsIgnoreCase(pathSegments[0])) { 
        ...invoke rest service based on information extracted from path segment values and build synthetic resources based on results... 
        dropdownOptions = ...set string array to results of rest invocation, formatted as needed... 
       } else if (PROPERTIES_SEGMENT_NAME.equalsIgnoreCase(pathSegments[0])) { 
        ...read property file based on information extracted from path segment values and build synthetic resources based on results... 
        dropdownOptions = ...set string array to results of parsing property file, formatted as needed... 
       } 

       String propsPath = providerRootPrefix + StringUtils.join(Arrays.copyOfRange(pathSegments, 0, pathSegments.length - 1), "/"); 

       return new SyntheticResource(resourceResolver, propsPath, "sling:Folder/" + pathSegments[pathSegments.length - 1]) { 
        public <T> T adaptTo(Class<T> type) { 
         return (T) dropdownOptions; 
        } 
       }; 
      } 

      return null; 
     } 
    } 

    protected void deactivate() { 
     this.providerRoot = null; 
     this.providerRootPrefix = null; 
    } 
} 

这让我然后进入外的现成的下拉组件的编辑组件对话框,并设置项目加载路径,在我的资源提供者会回答的路径。你可以在下面的例子中看到,这将指向一个属性文件,其内容是允许用户从中选择的国家列表。将这些内容存储在存储库中并不是必需的,这为作者指出已知资源(属性,REST服务,无论您需要什么)提供了一种简单,动态的方式,并且可以轻松填充下拉列表,而无需构建自定义组件或无需在存储库中输入数百个项目。

enter image description here

+0

不错的例子。你能分享上面例子的完整的类/代码吗? – 2015-11-02 22:12:12

0

该组件的JSP模板只需迭代下拉内的键值&的映射。 JSON可能不是将地图传递给JSP的最有效方法 - 更好地让服务直接返回地图而不是序列化为JSON,然后在组件中对其进行反序列化。

一个HTML选择组件,提供状态的其自身的(静态)映射(可以提供地图的替代源)的一个例子:

<% 
final Map<String, String> stateMap = new HashMap<String, String>(){{ 
    put("Alabama","AL"); 
    put("Alaska","AK"); 
    put("Arizona","AZ"); 
    ... 
}}; 
List<String> stateList = new ArrayList<String>(); 
stateList.addAll(stateMap.keySet()); 
Collections.sort(stateList); 

final String selectedState = (String) request.getAttribute("state-selected"); 
%><c:set var="name" value="<%= resource.getName() %>" /> 
<c:set var="selectedState" value="<%= selectedState %>" /> 
<c:set var="stateMap" value="<%= stateMap %>" /> 
<label for="${name}">${label}</label> 
<select id="${name}" name="${name}"> 
    <option value=""><%= properties.get("select", "Select Your State") %></option> 
    <c:forEach var="state" items="<%= stateList %>"> 
     <option value="${stateMap[state]}" ${ selectedState==stateMap[state] ? " selected" : ""}>${state}</option> 
    </c:forEach> 
</select> 

同样,“地图”不必是一个常量 - 它可以通过解析JSON响应或读取资源的属性集来构建,或者......为了真正动态化,对话框可能会提示输入JSON的url,但是更好的办法是只有OSGi提供数据的服务。

+0

感谢您的建议。我曾考虑过使用这种方法,但我所寻找的是更通用的东西。换句话说,我希望能够使用开箱即用的组件并提供一个路径(optionsLoadPath)并让它自动填充所需的数据。这对于希望在页面上拖动组件但不编写自定义组件的页面作者特别有用。我确实找到了我正在寻找的解决方案。请参阅下面的答案。 – Steve 2014-11-20 19:19:59