2

ログインフォーム複合コンポーネントを構築しています。それを使用するページは、ユーザー名とパスワードを検証するイベント ハンドラーを渡します。通常 (複合コンポーネントを使用しない場合) を介してクロス フィールド検証を実行する場合postValidate、イベント ハンドラはフィールドのコンポーネントを名前で検索する必要があります。これらは抽象化する必要があるコンポーネントの内部の詳細であるため、バリデーターがこれを行わないことが望ましいでしょう。

postValidate複合コンポーネントの内部の詳細を知らなくても、ハンドラーでユーザー名とパスワードのフィールドの変換された値を取得する方法はありますか?

更新: これのポイントは、コンポーネントを名前で検索することをまったく避けることではなく、使用中のページや Bean が内部を知る必要がない方法で、複合コンポーネントのフィールドをクロスフィールド検証できるようにすることです。コンポーネントの詳細。

4

3 に答える 3

3

これは可能です。postValidate次のコードでは、複合コンポーネントのイベントとpostValidateバッキング コンポーネントのメソッドに特に注意してください。MethodExpression属性を解決し、渡されたメソッドを呼び出す方法に注意してください。

複合コンポーネントは次のとおりです。

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:cc="http://java.sun.com/jsf/composite"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui">

    <!-- Login form. -->
    <cc:interface componentType="com.example.LoginForm">
        <cc:attribute name="emailAddress" type="java.lang.String" required="true"/>
        <cc:attribute name="rememberMe" type="java.lang.Boolean" required="true"/>
        <cc:attribute name="checkCredentials"
                      method-signature="void checkCredentials(java.lang.String,java.lang.String,java.lang.String)"
                      shortDescription="Parameters are clientId, username and password. If credentials are invalid, attach a FacesMessage to the component specified by clientId."
                      required="true"/>
        <cc:attribute name="actionListener" method-signature="void actionListener()" required="true"/>
        <cc:attribute name="registerOutcome" type="java.lang.String" required="true"/>
        <cc:attribute name="recoverPasswordOutcome" type="java.lang.String" required="true"/>
        <cc:attribute name="headerTitle" type="java.lang.String" default="Sign In"/>
        <cc:attribute name="emailAddressLabel" type="java.lang.String" default="Email address:"/>
        <cc:attribute name="passwordLabel" type="java.lang.String" default="Password:"/>
        <cc:attribute name="rememberMeLabel" type="java.lang.String" default="Stay signed in on this machine"/>
        <cc:attribute name="loginLabel" type="java.lang.String" default="Sign In"/>
        <cc:attribute name="recoverPasswordLabel" type="java.lang.String" default="Forgot password?"/>
        <cc:attribute name="emailAddressRequiredMessage" type="java.lang.String" default="Email address required"/>
        <cc:attribute name="passwordRequiredMessage" type="java.lang.String" default="Password required"/>
        <cc:attribute name="registerLabel" type="java.lang.String" default="Register"/>
    </cc:interface>

    <cc:implementation>
        <h:outputStylesheet library="components/example/login-form" name="style.css"/>

        <div id="#{cc.clientId}">
            <h:form id="form">

                <f:event type="postValidate" listener="#{cc.postValidate}"/>

                <div style="margin-top:10px;">
                    <p:panel header="#{cc.attrs.headerTitle}" styleClass="loginPanel">
                        <div class="login-form_errorContainer">
                            <p:messages rendered="#{facesContext.maximumSeverity.ordinal ge 2}"/>
                        </div>
                        <h:panelGrid columns="3">
                            <h:outputText styleClass="login-form_label" value="#{cc.attrs.emailAddressLabel}"/>
                            <h:panelGroup styleClass="login-form_cell">
                                <h:inputText id="emailAddress"
                                             value="#{cc.attrs.emailAddress}"
                                             required="true"
                                             requiredMessage="#{cc.attrs.emailAddressRequiredMessage}"
                                             styleClass="login-form_field"
                                             immediate="true"/>
                            </h:panelGroup>
                            <h:panelGroup/>

                            <h:outputText styleClass="login-form_label" value="#{cc.attrs.passwordLabel}"/>
                            <h:panelGroup styleClass="login-form_cell">
                                <h:inputSecret id="password"
                                               value="#{cc.attrs.password}"
                                               required="true"
                                               requiredMessage="#{cc.attrs.passwordRequiredMessage}"
                                               styleClass="login-form_field"
                                               immediate="true"/>
                            </h:panelGroup>
                            <h:link styleClass="login-form_link" value="#{cc.attrs.recoverPasswordLabel}" outcome="#{cc.attrs.recoverPasswordOutcome}"/>

                            <h:panelGroup/>
                            <p:selectBooleanCheckbox value="#{cc.attrs.rememberMe}" itemLabel="#{cc.attrs.rememberMeLabel}" immediate="true"/>
                            <h:panelGroup/>

                            <h:panelGroup/>
                            <h:panelGroup>
                                <p:commandButton id="submitForm" value="#{cc.attrs.loginLabel}" actionListener="#{cc.attrs.actionListener}" update="form"/>
                                <span class="login-form_or">or</span>
                                <h:link styleClass="login-form_link" value="#{cc.attrs.registerLabel}" outcome="#{cc.attrs.registerOutcome}"/>
                            </h:panelGroup>
                            <h:panelGroup/>
                        </h:panelGrid>
                    </p:panel>
                </div>
            </h:form>
        </div>
    </cc:implementation>
</html>

バッキング コンポーネント:

@FacesComponent("com.example.LoginForm")
public class LoginFormComponent extends UIInput implements NamingContainer
{
    @Override
    protected Object getConvertedValue(FacesContext context, Object newSubmittedValue) throws ConverterException
    {
        UIInput emailAddressComponent = (UIInput) findComponent(EMAIL_ADDRESS_ID);
        UIInput passwordComponent = (UIInput) findComponent(PASSWORD_ID);
        String emailAddress = (String) emailAddressComponent.getValue();
        String password = (String) passwordComponent.getValue();
        return new LoginFormValue(emailAddress, password);
    }

    public void postValidate(ComponentSystemEvent e) {
        FacesContext ctx = getFacesContext();

        // Don't validate credentials if the username and/or password fields are invalid.
        if (!ctx.getMessageList(EMAIL_ADDRESS_ID).isEmpty() || !ctx.getMessageList(PASSWORD_ID).isEmpty())
        {
            return;
        }

        LoginFormValue value = (LoginFormValue) getConvertedValue(null, null);
        MethodExpression checkCredentials = (MethodExpression) getAttributes().get(CHECK_CREDENTIALS_ATTRIBUTE_NAME);
        checkCredentials.invoke(ctx.getELContext(), new Object[]{getClientId(), value.getEmailAddress(), value.getPassword()});
    }

    @Override
    public String getFamily()
    {
        return "javax.faces.NamingContainer";
    }

    public static final String CHECK_CREDENTIALS_ATTRIBUTE_NAME = "checkCredentials";
    public static final String EMAIL_ADDRESS_ID = "form:emailAddress";
    public static final String PASSWORD_ID = "form:password";
}

完全性のためのLoginFormValueクラス:

public class LoginFormValue
{
    public LoginFormValue(String emailAddress, String password)
    {
        this.emailAddress = emailAddress;
        this.password = password;
    }

    public String getEmailAddress()
    {
        return emailAddress;
    }

    public String getPassword()
    {
        return password;
    }

    private String emailAddress;
    private String password;
}

ログインフォームを使用するページ:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ex="http://java.sun.com/jsf/composite/components/example">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/myLayout.xhtml">
            <ui:define name="windowTitle">Sign In</ui:define>
            <ui:define name="body">

                <ex:login-form emailAddress="#{loginBean.emailAddress}"
                               rememberMe="#{loginBean.rememberMe}"
                               checkCredentials="#{loginBean.checkCredentials}"
                               actionListener="#{loginBean.submit()}"
                               recoverPasswordOutcome="recover-password"
                               registerOutcome="signup"/>

            </ui:define>
        </ui:composition>
    </h:body>
</html>

最後に、ページのバッキング Bean:

@Named
@RequestScoped
public class LoginBean implements Serializable
{
    public String getEmailAddress()
    {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress)
    {
        this.emailAddress = emailAddress;
    }

    public boolean isRememberMe()
    {
        return rememberMe;
    }

    public void setRememberMe(boolean rememberMe)
    {
        this.rememberMe = rememberMe;
    }

    /** Action listener for login-form. Called after validation passes. */
    public void submit()
    {
        User user = userDao.findByEmailAddress(emailAddress);
        userRequestBean.login(user.getUserId());

        // Remember me
        if (!rememberMe)
        {
            return;
        }

        // Handle rememberMe here (create a cookie, etc.)
    }

    /** Called by the backing component's postValidate event handler */
    public void checkCredentials(String clientId, String emailAddress, String password)
    {
        if (!securityEjb.checkCredentials(emailAddress, password))
        {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Incorrect email address/password", null);
            FacesContext ctx = FacesContext.getCurrentInstance();
            ctx.addMessage(clientId, message);
            ctx.renderResponse();
        }
    }

    private String emailAddress = "";

    private boolean rememberMe = true;

    @Inject
    private UserRequestBean userRequestBean;

    @EJB
    private SecurityEjb securityEjb;

    @EJB
    private UserDao userDao;

    @EJB
    private LoginCookieDao loginCookieDao;
}
于 2012-06-10T05:36:04.847 に答える
0

このf:event postValidateアプローチでは、多くのオプションが残されません。

私が好むオプションは、フォームの最後のコンポーネントで検証を行い、バインディングと f:attribute を使用して他のコンポーネントを渡すことです。

例えば

<h:inputText id="field1" binding="#{field1}" ... />
<h:inputText id="field2" validator="#{...}">
  <f:attribute name="field1" value="#{field1}"/>
</h:inputText>

次に、バリデーターで、UIInput から他のコンポーネントを取得できます。

UIComponent field1 = field2.getAttributes().get("field1")
于 2012-06-10T01:30:30.130 に答える
0

同様の問題にJsfWarnを使用しましたが、問題をよりクリーンな方法で解決できると思います。

JSF バリデーターとは異なり、WarningValidators は、モデルが更新されてアプリケーションが呼び出された後、render-response の前に実行されるため、アプリケーションにアクセスして検証結果を確認するだけで済みます。

@Named
public class BarWarningValidator implements WarningValidator{

    @Inject
    private MyValidationBean validationBean;

    @Override
    public void process(FacesContext context, UIInput component, ValidationResult validationResult) {
        if(!validationBean.isBarValid()) {
            validationResult.setFacesMessage(new FacesMessage(FacesMessage.SEVERITY_WARN, "FooBar", "This is a warning."));
        }
    }
}

そして、バリデーターを対象フィールドに追加します。

<h:outputLabel for="bar" value="Default warning:" />
<h:inputText id="bar">
    <jw:warning validator="#{barWarningValidator}" />
    <f:ajax event="change" render="..." />
</h:inputText>
<h:message for="bar" />
于 2015-07-06T21:36:44.933 に答える