displayname displayname - 3 months ago 23
Javascript Question

Uncaught TypeError: FormNapper requires an HTMLFormElement element or the id string of one

I'm having a form that is simply not working as I try to make it ready for braintree in order to create a custom form for credit card information.

However, if I'm creating this form in the UiBinder:

<g:HTML ui:field="creditCardFormContainer">
<form id="credit-card-form" name="credit-card-form" action="/app/create-credit-card" method="post" target="hidden-iframe">

<label for="card-number">Card Number</label>
<div id="card-number"/>

<label for="cvv">CVV</label>
<div id="cvv"/>

<label for="expiration-date">Expiration Date</label>
<div id="expiration-date"/>

<input id="nonce" name="nonce" type="hidden" value="" />
<input type="submit" value="Add credit card" />
</form>
</g:HTML>


and make a check of the type
credit-card-form
I'm actually seeing that it isn't the type I'm expecting it to be:

private native void setupCreditCardForm(String domId, String serverToken) /**/-{

var form = $doc.getElementById(domId);

if (form instanceof HTMLFormElement != true) {
console.log(typeof(form)); // Prints 'object'
throw new TypeError("Form must be of type HTMLFormElement"); // Gets thrown ..
}

// Never reached ..

var bt = braintree;

braintree.setup(
serverToken,
"custom",
{
id: domId,
hostedFields: {
number: {
selector: "#card-number"
},
cvv: {
selector: "#cvv"
},
expirationDate: {
selector: "#expiration-date"
}
},
onPaymentMethodReceived: function(details) {
this.@com.mahlzeit.client.admin.widget.paymentmethodinfo.PaymentMethodInfoView::onPaymentMethodReceived(Ljava/lang/String;)(details.nonce);
}
});
}-/**/;


Why is this the case? I'm clearly creating a
form
here - why is this not working out?

I'm doing it like here as well as described in the documentation.

Removing this check above would give me the following error coming from
braintree.setup()
:

FormNapper requires an HTMLFormElement element or the id string of one.

Answer

braintree should be$wnd.braintree: if Braintree runs into hidden iframe gwt runs in, it cannot see the form through its ID. Passing the form's element might work, but Braintree should be injected into the "top window", not GWT's iframe.

Similarly, instanceof doesn't work because of iframe boundaries: use $wnd.HTMLFormElement.

Regarding the injection of the Braintree JavaScript code:

You need to tell ScriptInjector to inject into the TOP_WINDOW. For example:

braintreeJs = ScriptInjector.fromUrl(BRAINTREE_JS_URL).setCallback(
    new Callback<Void, Exception>() {
        @Override
        public void onFailure(Exception caught) {
            // ..
        }
        @Override
        public void onSuccess(Void result) {
            // ..
        }
    }).setWindow(ScriptInjector.TOP_WINDOW).inject();
Comments