使用 JavaScript 框架注册 JSF 自定义组件

Registering JSF custom components with JavaScript framework

我正在努力将前端组件的 JavaScript 框架 (Aurelia) 集成到我们现有的 JSF 应用程序中。我正在使用 JSF 渲染器将自定义标签放入输出中,例如:

@FacesRenderer(componentFamily = "frontend.component", rendererType = "frontend.mytag")
public class MyTagRenderer extends Renderer {

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        final String tag = "my-tag";
        int id = (Integer) component.getAttributes().get("id");
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(tag, component);
        writer.writeAttribute("id.bind", id, null);
        writer.endElement(tag);
    }
}

问题是在初始化 JS 框架时我必须知道需要 bootstrapped 哪些自定义标签。起初我尝试在服务器端执行此操作,方法是将标记名作为 属性 添加到我的 JSF 前端组件 class,将其设置在自定义渲染器中并遍历 JSF 组件树以获取列表:

class MyTagRenderer {
    public void encodeEnd(FacesContext context, UIComponent component) {
        ...
        ((FrontendComponent) component).setTagName("my-tag:);
    }
}

@ManagedBean
@RequestScoped
class FrontendBootstrapHandler {
    public String getTagList() {
        List<String> tags = walkRecursive(FacesContext.getCurrentInstance().getViewRoot());
        return "'" + String.join("','", tags + "'";
    }
}

但是我发现 JSF 呈现部分页面的顺序并不完全可以预测,有时 walkRecursivesetTagName 之前被调用。

现在我已经决定在每个自定义标签旁边呈现一个 <script> 片段,以将标签列表保存到 bootstrap 客户端,但这并不理想。谁能给我一个更优雅的解决方案?

我找到了一种在服务器端维护组件列表的方法。我没有在渲染器中将标签名称设置为 bootstrap,它可以随时调用,我将其设置在 ComponentHandler:

my.taglib.xml

<tag>
    <tag-name>my-tag</tag-name>
    <attribute>
        <name>id.bind</name>
        <required>true</required>
        <type>java.lang.Integer</type>
    </attribute>
    <component>
        <component-type>frontend.component</component-type>
        <renderer-type>frontend.renderer</renderer-type>
        <handler-class>frontend.FrontendComponentHandler</handler-class>
    </component>
</tag>

FrontendComponentHandler.java

public class FrontendComponentHandler extends javax.faces.view.facelets.ComponentHandler {

    public FrontendComponentHandler(ComponentConfig config) {
        super(config);
    }

    @Override
    public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
        FrontendComponent component = (FrontendComponent) c;
        component.setTagName(tag.getLocalName());
    }
}

FrontendBootstrapHandler.java

@ManagedBean
@RequestScoped
class FrontendBootstrapHandler {
    public String getTagList() {
        List<String> tags = walkRecursive(FacesContext.getCurrentInstance().getViewRoot());
        return "'" + String.join("','", tags + "'";
    }
}