从JSON数据填充页面(不在对话框中)上的下拉列表

从JSON数据填充页面(不在对话框中)上的下拉列表,json,servlets,aem,Json,Servlets,Aem,我是CQ的新手。我想做的是能够从服务或servlet中检索的数据填充页面上的一个现成的下拉列表组件 我见过从servlet在编辑对话框中填充字段的解决方案,但没有在页面本身中。我知道我可以手动将每一行添加到下拉列表的编辑面板中的下拉列表中。我还看到了Items加载路径,我可以为类型为String[]的节点属性提供路径,其中属性的每个元素的格式为key=value。这两种解决方案都是非常手动的,对于需要某种程度上动态的列表没有提供灵活性 我知道我也可以定义一个servlet路径并通过ajax调用手

我是CQ的新手。我想做的是能够从服务或servlet中检索的数据填充页面上的一个现成的下拉列表组件

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

我知道我也可以定义一个servlet路径并通过ajax调用手动加载下拉列表……但是这些数据是静态的,为了提高效率,可以/应该在构建页面时检索这些数据,而不是再次往返到服务器。在我看来,应该有一种方法将下拉列表绑定到一些JSON数据,这些数据在servlet或服务中动态构建,并在构建页面时填充到下拉列表中。也许将项目加载路径指向其资源类型以某种方式绑定到servlet的节点


我对CQ是如此的陌生,以至于我很难想出如何将这些东西结合在一起,但是,用数据填充下拉列表似乎是一种相当常见的需求,我不必手动将数据硬编码到页面或节点中。

组件的JSP模板只需在下拉列表中的键和值映射上迭代即可。JSON可能不是将映射传递给JSP的最有效方法——最好让服务直接返回映射,而不是序列化为JSON,然后在组件中反序列化它

提供自己的静态状态映射的HTML select组件示例可以提供映射的替代源:

<%
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服务来提供数据。

我不知道我写这个问题时的措辞是否恰当。从那以后我学到了一些东西,所以我想我想说的是,我想有一种方法来生成合成节点,这些节点不是真正在JCR中支持的,而是动态构建的。我最终想出的办法是写一篇文章。我的具体用例是为作者提供一种简单的方法,通过来自两个地方的数据填充下拉组件:

打包的属性文件 调用外部REST资源 我对我的解决方案的灵感主要来自这篇文章:

这是我写的大部分课程。我忽略了从属性文件和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服务以及您需要的任何内容,并且可以轻松地填充下拉列表,而无需构建自定义组件或在存储库中输入数百项


谢谢你的建议。我曾考虑过使用这种方法,但我想要的是更通用的方法。换句话说,我希望能够使用开箱即用的组件,并为其提供路径选项LoadPath,使其自动填充所需的数据。这对于希望在页面上拖动组件但不编写自己的自定义组件的页面作者特别有用。我确实找到了我想要的解决办法。请看下面我的答案。很好的例子。您可以分享上面示例的完整类/代码吗。