How do I send a POST request with all parameters from a form using an AngularJS directive?

I have made an AngularJS form that integrates Stripe thru a directive. Fiddle here: https://jsfiddle.net/u5h1uece/.

HTML:

<body ng-app="angularjs-starter">
  <script src="https://js.stripe.com/v3/"></script>
  <div ng-controller="MainCtrl">
    <form name="regForm" id="register-form">
      <label>Mail</label>
      <input ng-model="reg.email" type="email" name="username">
      <div stripe-validator 
           stripe-complete="stripeCompleted" 
           stripe-form-id="register-form"></div>
      <br>
      <button ng-model="reg.btn" ng-disabled="stripeCompleted === false || !regForm.username.$valid">Register</button>
    </form>
  </div>
</body>

JS:

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope, $rootScope) {
  //Init stripe state via controller
  $scope.stripeCompleted = false;
});


app.directive('stripeValidator', function() {
  return {
    restrict: 'A',
    template: `
      <div id="stripe-wrapper">
        <div id="card-element"></div>
      </div>
      <small id="card-errors" class="text-danger" role="alert">{{ ccErrMsg }}</small>
      <input type="hidden" name="stripeToken" ng-value="stripeToken" />`,
    scope: {
      "stripeComplete": '=',
      "stripeFormId": '@',
      "stripeError": '=',
      "stripeToken": '=',
    },
    link: function(scope, element, attrs) {

      //Init
      var stripe = Stripe("pk_test_6pRNASCoBOKtIshFeQd4XMUh");
      var elements = stripe.elements();
      var card = elements.create("card");
      var form = document.getElementById(scope.stripeFormId);

      //mount card element https://stripe.com/docs/stripe-js/reference#element-mount
      card.mount("#card-element");

      //add event listener
      card.addEventListener('change', function(event) {

        //check for errors
        if (event.error) {
          scope.ccErrMsg = event.error.message;
        } else {
          scope.ccErrMsg = '';
        }

        //check for complete
        scope.stripeComplete = event.complete ? true : false;

        //apply scope
        scope.$apply();
      });

      //inject form submit event
      form.addEventListener("submit", function(event) {

        //prevent form submit
        event.preventDefault();

        //handle token, handle error and form submit forward
        stripe.createToken(card).then(function(result) {

          if (result.error) {
             scope.ccErrMsg = event.error.message;
             scope.stripeToken = '';
          } else {
             scope.ccErrMsg = '';
             scope.stripeToken = result.token;
          }

          //apply scope
          scope.$apply();

          //forward submit
          form.submit();
        })
      });
    }
  }
});

Instead of using form.submit(), I want to use $http.post() to send the form parameters and data to my backend, then send back any data from there in a .then() function. In this case, the parameters include reg.email's value.

How do I do this with the way my form is set up? I'd prefer using ng-submit, but if that's not possible, is there another way?

Answers:

Answer

I would suggest using a component to wrap the stripe input. You can make this component work with the ng-model directive to pass the card element back to the parent scope as well as apply form validation to the stripe field.

TL;DR - Here's an example Fiddle

First thing you'll want to do is create your stripe object as a service so it can be shared.

app.service('stripe', function () {
    return Stripe("pk_test_6pRNASCoBOKtIshFeQd4XMUh");
});

Then you can implement the stripe component to wrap around the stripe input and apply custom ng-model validation.

app.component('stripe', {
    bindings: {
        state: '='
    },
    require: {
        model: 'ngModel'
    },
    controller: function ($element, $timeout, stripe) {
        this.$onInit = function () {
            var ctrl = this;

            // stripe is passed in to controller as a service
            var elements = stripe.elements();
            var card = elements.create('card');
            card.mount($element[0]);

            card.addEventListener('change', function (event) {
                // Attach the event as the state so it is visible
                // to consumers of the stripe component.
                ctrl.state = event;

                // Set the validity of the stripe on the ng-model
                ctrl.model.$setValidity('stripe', event.complete);

                // If the stripe is complete set the card element on 
                // the ng-model, otherwise null out the model value.
                if (event.complete) {
                    ctrl.model.$setViewValue(card);
                } else {
                    ctrl.model.$setViewValue(null);
                }
            });
        }
    }
});

Then in the main controller you can create the stripe token when the form is submitted before you send off the HTTP request.

app.controller('MainCtrl', function ($scope, $http, stripe) {
    $scope.reg = {};
    $scope.onChange = function() {
        console.log($scope.card);
    };
    $scope.register = function () {
        // Create the stripe token and send the registration
        stripe.createToken($scope.card).then(function (result) {
            if (result.error) {
                console.error(result.error);
            } else {
                $scope.reg.stripeToken = result.token;
                $http.post('request-url', $scope.reg).then(function (response) {
                    console.log(response);
                }).catch(function (err) {
                    console.error(err);
                });
            }
        });
    };
});

Now you can simply add a stripe element to your template as follows:

<div ng-app="angularjs-starter" ng-controller="MainCtrl">
  <form name="regForm" id="register-form" ng-submit="register()">
    <stripe name="card" ng-model="card" state="cardState" ng-change="onChange()" ng-required="true"></stripe>
    <button ng-disabled="regForm.$invalid">Register</button>
    <span ng-if="regForm.card.$invalid">{{ cardState.error.message }}</span>
  </form>
</div>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.