使用h:commandButton和Javascript进行JSF导航

时间:2017-04-24 20:57:41

标签: javascript jsf jsf-2 stripe-payments

我试图将Stripe集成到我的JSF应用程序中,并且很难导航离开"添加信用卡"页。一切正常,除非用户点击提交后,页面不会离开。

以下是addCreditCard.xhtml facelet。添加javascript逻辑作为提交eventListener并使用onclick="#{stripeCCBean.update()}"触发bean更新方法()是我获得javascript成功创建令牌的唯一方法(如果jc由onclick触发,则createToken方法将失败一些未知的原因)并让bean识别隐藏的字段。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui"
      template="/WEB-INF/template.xhtml"
      xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
      xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
    <head>
        <title>Facelet Title</title>
        <link rel="stylesheet" type="text/css" href="/css/StripeCCTokenize.css"/>
        <script src="https://js.stripe.com/v3/" type="text/javascript"></script>
        <script src="https://code.jquery.com/jquery-3.2.0.js" type="text/javascript"></script>
        <script src="/js/StripeCCTokenize.js" type="text/javascript"></script>
    </head>
    <h:body>
        <h:form id="addCC" pt:action="/secure/addCreditCard.xhtml" pt:method="POST">

        <h:inputHidden id="cardholder-name" value="#{userManagerBean.user.fullName}"/>

        We loaded your customer details (name, email and customer ID) from the backend database:

        <label>
            Hello #{userManagerBean.user.firstName} #{userManagerBean.user.lastName}
        </label>
        <label>
            E-Mail - #{userManagerBean.user.email}
        </label>
        <label>
            Stripe Customer ID - #{userManagerBean.stripeUser.id}
        </label>
        <h:outputText value="Please enter the requested credit card and billing information below"/>
        <span>Address</span>
        <h:panelGrid columns="2">
            <h:outputText value="Address" />
            <h:inputText class="field" id="address1" value="#{stripeCCBean.card.address1}" pt:placeholder="Street address"/>
            <h:outputText value="Address"/>
            <h:inputText class="field" id="address2" value="#{stripeCCBean.card.address2}" pt:placeholder="Street address"/>
            <h:outputText value="City" />
            <h:inputText class="field" id="city" value="#{stripeCCBean.card.city}" pt:placeholder="city"/> 
            <h:outputText value="State" />
            <h:inputText class="field" id="state" value="#{stripeCCBean.card.state}" pt:placeholder="state"/>
            <h:outputText value="zip" /> 
            <h:inputText class="field" id="address-zip" value="#{stripeCCBean.card.zipcode}" pt:placeholder="zipcode"/>
            <h:outputText value="cc"/>
        </h:panelGrid>
        <div id="card-element" class="field"></div>

        <h:commandButton value="Add Credit Card" onclick="#{stripeCCBean.update()}" type="submit" id="addButton"/>
    </h:form>
</h:body>

这是StripeCCTokenize.js:

var stripe; var card;

$(document).ready(function () {
    stripe = Stripe('pk_test_key');
    var elements = stripe.elements();

card = elements.create('card', {
    hidePostalCode: true,
    style: {
        base: {
            iconColor: '#F99A52',
            color: '#32315E',
            lineHeight: '48px',
            fontWeight: 400,
            fontFamily: '"Helvetica Neue", "Helvetica", sans-serif',
            fontSize: '15px',
            '::placeholder': {
                color: '#CFD7DF'
            }
        }
    }
});
card.mount('#card-element');

function stripeTokenHandler(token) {
    // Insert the token ID into the form so it gets submitted to the server
    var form = document.getElementById('addCC');
    var hiddenInput = document.createElement('input');
    hiddenInput.setAttribute('type', 'hidden');
    hiddenInput.setAttribute('name', 'stripeToken');
    hiddenInput.setAttribute('value', token.id);

    form.appendChild(hiddenInput);

    // Submit the form
    form.submit();
}

function setOutcome(result) {
    if (result.token) {
        // Use the token to create a charge or a customer
        // https://stripe.com/docs/charges

        console.log("Token: " + result.token.id);
        stripeTokenHandler(result.token);

    } 
}

card.on('change', function (event) {
    setOutcome(event);
});

document.querySelector('form').addEventListener('submit', function (e) {
    e.preventDefault();

    var extraDetails = {
        address_line1: document.getElementById('addCC:address1').value,
        address_line2: document.getElementById('addCC:address2').value,
        address_city: document.getElementById('addCC:city').value,
        address_state: document.getElementById('addCC:state').value,
        address_zip: document.getElementById('addCC:address-zip').value,
        name: document.getElementById('addCC:cardholder-name').value
    };
    console.log(extraDetails);
    stripe.createToken(card, extraDetails).then(setOutcome);
    });
});

这是stripeCCBean类:

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import lombok.Data;
import lombok.ToString;

@Data
@ToString
@RequestScoped
@ManagedBean(name = "stripeCCBean")
public class StripeCCBean implements Serializable {

    StripeCard card;

    @ManagedProperty(value = "#{stripeServiceBean}")
    private StripeServiceBean stripeServiceBean;

    @ManagedProperty(value = "#{userManagerBean}")
    private UserManagerBean userManagerBean;

    @PostConstruct
    public void init() {
        System.out.println("StripeCCBean.init()");
        card = new StripeCard();
        card.setName(userManagerBean.getUser().getFullName());
    }

    public void update() throws IOException {
        String token = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("stripeToken");

        if (token == null) {
            return;
        }

        System.out.println("StripeCCBean.update()");

        System.out.println("Token: " + token);
        System.out.println("Card: " + card);

        try {
            StripeService.addCard(userManagerBean.getStripeUser().getId(), token);
        } catch (AuthenticationException | APIConnectionException | CardException | APIException | InvalidRequestException ex) {
            ex.printStackTrace();
        }

    }
}

我尝试将action="#{stripeCCBean.viewAccount()}"添加到<h:commandButton .../>以及StripeCCBean的相应方法:

public String viewAccount() {
    return "AccountView";
}

然而,表单只是运行Javascript,调用stripeCCBean.update()(一切正常),然后停留在该页面上。客户信息字段不会被清除,但信用卡元素会清除。

我尝试添加FacesContext.getCurrentInstance().getExternalContext().redirect("/secure/AccountView.xhtml"); 以及 对于stripeCCBean.update()方法FacesContext.getCurrentInstance().getExternalContext().dispatch("/secure/AccountView.xhtml");并且都不起作用。事实上,他们抛出异常。

谁能看到我做错了什么?如果我错误或低效地触发JS,我也很乐意改变它。

3 个答案:

答案 0 :(得分:0)

另外一个“我不知道为什么会这样”,但我尝试将FacesContext.getCurrentInstance().getExternalContext().redirect("/secure/AccountView.xhtml");添加到stripeCCBean.update()方法的末尾,现在它可以正常工作。

仍然不知道为什么我必须通过commandButton的onclick调用update()方法,但是...

答案 1 :(得分:0)

请删除preventDefault函数,该函数会阻止浏览默认行为,您无需使用onclick事件。

答案 2 :(得分:0)

如果您想通过f:commandButton / f:commandLink导航,请使用action属性而不是onclick。

<f:commandButton ... action="#{myBean.actionHandler}".../>

如果您不使用任何ajax机制,则​​bean可以是@RequestScoped,其他情况@ViewScoped

@Named
@RequestScoped
public class MyBean
{
  public String actionHandler()
  {
    ...
    return "navigationRuleName";
  }

  ...
}

navigationRuleName中注册了faces-config.xml

<navigation-rule>
  <from-view-id>/path/source.xhtml</from-view-id>
  <navigation-case>
    <from-outcome>navigationRuleName</from-outcome>
    <to-view-id>/path/targetPageName.xhtml</to-view-id>
    <redirect/>
  </navigation-case>
</navigation-rule>

或者它可以是页面名称:

@Named
@RequestScoped
public class MyBean
{
  public String actionHandler()
  {
    ...
    return "/path/targetPageName.xhtml?faces-redirect=true";
  }

  ...
}

或者可以省略xhtml扩展名。

&#39;&#39;如果您希望在HTTP POST请求 - 响应导航后,浏览器的位置URL更改为targetPageName.xhtml,则需要实体或faces-redirect=true参数。重定向通过HTTP POST后的另一个HTTP GET调用来完成。