1

I have a form with credit card fields and billing address fields. It's bound to some angular models and works beautifully, until I try to fill in the form fields with password managers (I tried 1Password and RoboForm).

With the form below, I see the following behavior:

  • 1Password: credit card info is entered correctly, but it also puts in the credit card number in the name, company, and street address fields, and the expiration month in city and state.
  • RoboForm: fills the CVC field with the expiration year and the zip code field with the state

Here is the form:

<form class="form" rc-submit="submitCC()" name="ccForm" id="ccForm" novalidate>
    <div class="cardback">
        <div class="title">Credit Card Details</div>
        <div class="form-row with-label" ng-class="{'has-error': numberInvalid || (ccForm.ccNumber.$invalid &amp;&amp; rc.ccForm.attempted)}">
            <input id="cc-number" class="form-control" type="text" name="ccNumber" ng-model="card.number" data-stripe="number" required size="20" autocomplete="cc-number" />
            <label class="form-label">card number</label>
            <div class="validation-error" ng-show="numberInvalid">the credit card number is invalid</div>
        </div>
        <div class="form-row for-inline">
            <div class="form-row inline with-label month" ng-class="{'has-error': ccForm.ccExpMonth.$invalid &amp;&amp; rc.ccForm.attempted}">
                <select id="cc-exp-month" class="form-control" ng-model="card.expMonth" name="ccExpMonth" data-stripe="exp-month" ng-options="m for m in expMonths" required autocomplete="cc-exp-month">
                    <option value="">MM</option>
                </select>
                <label class="form-label">exp month</label>
                <div class="select-arrow"></div>
            </div>
            <div class="form-row inline with-label year" ng-class="{'has-error': ccForm.ccExpYear.$invalid &amp;&amp; rc.ccForm.attempted}">
                <select id="cc-exp-year" class="form-control" ng-model="card.expYear" name="ccExpYear" data-stripe="exp-year" ng-options="y for y in expYears" required autocomplete="cc-exp-year">
                    <option value="">YYYY</option>
                </select>
                <label class="form-label">exp year</label>
                <div class="select-arrow"></div>
            </div>
            <div class="form-row inline with-label cvc" ng-class="{'has-error': ccForm.ccCsc.$invalid &amp;&amp; rc.ccForm.attempted}">
                <input id="csc" class="form-control" type="text" name="csc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
                <label class="form-label">cvc</label>
            </div>
            <!--
            <input id="cc-csc" class="form-control" type="text" name="ccCsc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            <input id="securityCode" class="form-control" type="text" name="securityCode" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            <input id="csc" class="form-control" type="text" name="csc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            <input id="cvc" class="form-control" type="text" name="cvc" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            <input id="cardCode" class="form-control" type="text" name="cardCode" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            <input id="code" class="form-control" type="text" name="code" ng-model="card.cvc" data-stripe="cvc" required size="4" autocomplete="cc-csc"/>
            -->
        </div>
    </div>

    <div class="billing-title">Billing Address</div>
    <div class="form-row with-label" ng-class="{'has-error': ccForm.name.$invalid &amp;&amp; rc.ccForm.attempted}">
        <input id="name" class="form-control" type="text" name="name" required ng-model="card.billingName" autocomplete="name"/>
        <label class="form-label">name</label>
        <div class="validation-error" ng-show="ccForm.name.$invalid &amp;&amp; rc.ccForm.attempted">name required</div>
    </div>
    <div class="form-row with-label" ng-class="{'has-error': ccForm.organization.$invalid &amp;&amp; rc.ccForm.attempted}">
        <input id="organization" class="form-control" type="text" name="organization" ng-model="card.billingCompany" autocomplete="organization"/>
        <label class="form-label">company (optional)</label>
    </div>
    <div class="form-row with-label" ng-class="{'has-error': ccForm.addressLine1.$invalid &amp;&amp; rc.ccForm.attempted}">
        <input id="address-line1" class="form-control" type="text" name="addressLine1" required ng-model="card.billingAddress" autocomplete="address-line1"/>
        <label class="form-label">street address</label>
        <div class="validation-error" ng-show="ccForm.addressLine1.$invalid &amp;&amp; rc.ccForm.attempted">address required</div>
    </div>
    <div class="form-row with-label" ng-class="{'has-error': ccForm.addressCity.$invalid &amp;&amp; rc.ccForm.attempted}">
        <input id="address-level2" class="form-control" type="text" name="addressCity" required ng-model="card.billingCity" autocomplete="address-level2"/>
        <label class="form-label">city</label>
        <div class="validation-error" ng-show="ccForm.addressCity.$invalid &amp;&amp; rc.ccForm.attempted">city required</div>
    </div>
    <div class="form-row for-inline">
        <div class="form-row inline with-label state" ng-class="{'has-error': ccForm.state.$invalid &amp;&amp; rc.ccForm.attempted}">
            <input id="address-level1" class="form-control" type="text" name="state" ng-model="card.billingState" autocomplete="address-level1"/>
            <label class="form-label">state</label>
        </div>
        <div class="form-row inline with-label zip" ng-class="{'has-error': ccForm.postalCode.$invalid &amp;&amp; rc.ccForm.attempted}">
            <input id="zip" class="form-control" type="text" name="zip" required ng-model="card.billingZip" autocomplete="postal-code"/>
            <label class="form-label">zip</label>
            <div class="validation-error" ng-show="ccForm.postalCode.$invalid &amp;&amp; rc.ccForm.attempted">zip required</div>
        </div>
        <!--
        <input id="postal-code" class="form-control" type="text" name="postalCode" required ng-model="card.billingZip" autocomplete="postal-code"/>
        <input id="zipCode" class="form-control" type="text" name="zipCode" required ng-model="card.billingZip" autocomplete="postal-code"/>
        <input id="zip" class="form-control" type="text" name="zip" required ng-model="card.billingZip" autocomplete="postal-code"/>
        -->
        <div class="form-row inline with-label country" ng-class="{'has-error': ccForm.countryName.$invalid &amp;&amp; rc.ccForm.attempted}">
            <input id="country-name" class="form-control" type="text" name="countryName" value="USA" required ng-model="card.billingCountry" autocomplete="country-name"/>
            <label class="form-label">country</label>
            <div class="validation-error" ng-show="ccForm.countryName.$invalid &amp;&amp; rc.ccForm.attempted">country required</div>
        </div>
    </div>

    <div class="form-row spaced">
        <button class="constructive" type="submit" ng-disabled="ccInProgress">confirm payment</button>
    </div>
</form>

I tried several different ways to format the fields, including various name and id attributes, as well as the autocomplete field as defined here: https://html.spec.whatwg.org/multipage/forms.html#autofill. Chrome seems to understand the autocomplete attribute pretty well.

I also tried adding multiple variations of the CVC and zip code fields for testing, and if I have a multitude of these fields, RoboForm works correctly. But as soon as I remove all but any one of those fields, it stops working.

Any experience with that or links to documentation would be greatly appreciated.

Stefan Henze
  • 2,711
  • 23
  • 22

1 Answers1

0

1Password will respect HTML's autofill spec so <input type="text" id="ccexp" autocomplete="cc-exp" /> will hint 1Password to fill the selected credit card's expiry.

Having proper labels is helpful, you can either use <label for="ccexp">Expiry Date:</label><input type="text" id="ccexp" ...> or <label>Expiry Date: <input type="text" ...></label>.

autocomplete is preferred since it will help with more than just 1Password's filling.

PhilippeLM
  • 163
  • 1
  • 1
  • 9
  • I have autocomplete in all fields according to the spec that we both linked to (see the code above). Doesn't seem to work. – Stefan Henze Nov 09 '14 at 17:27
  • @StefanHenze is that generated HTML out of Javascript or is that sent from the server ? Can you post the final HTML (Select your page and do a "Show Source for Selection" or equivalent) ? – PhilippeLM Nov 10 '14 at 01:47
  • It's an AngularJS app. So yes, it's a giant Javascript file that contains HTML template definitions. Could that be the cause of it? When I inspect the page, it all looks like I defined it in the source. If you don't mind, you could sign up for a trial account at https://app.updatezen.com and pretend you want to purchase a subscription. That leads to the form. – Stefan Henze Nov 10 '14 at 11:17