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

时间:2014-10-31 20:09:57

标签: json servlets cq5

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

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

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

我对CQ如此陌生,以至于我无法弄清楚如何将这些事情联系在一起,但似乎相当普遍的需要用数据填充下拉列表#39; t必须手动硬编码到页面或节点中。

2 个答案:

答案 0 :(得分: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>

再次,&#34;地图&#34;不必是一个常量 - 它可以通过解析JSON响应,或者读取资源的属性集来构建,或者......真正动态,对话框可能会提示输入JSON的url,但是更好的是拥有提供数据的OSGi服务。

答案 1 :(得分:0)

当我写这篇文章时,我不知道我的问题得到了充分的回答。从那以后我学到了一些东西,所以我想我想说的是我想要一种方法来生成&#34;合成&#34;不是在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;
    }
}

这使我可以进入开箱即用的Dropdown组件的编辑组件对话框,并将Items Load Path设置为我的资源提供者将回答的路径。您可以在下面的示例中看到,这将指向一个属性文件,其内容是应允许用户选择的国家/地区列表。将这些存储在存储库中是不必要的,这为作者提供了一种简单,动态的方式来指向已知资源(属性,REST服务,无论您需要什么)并轻松填充下拉列表而无需构建自定义组件或无需在存储库中输入数百个项目。

enter image description here