Advanced Donation Form

The Advanced Donation Form Part and accompanying REST APIs were released in version 3.0 of BBIS. They allow completely custom interfaces to be created for the donation experience, relaying the necessary information to the backend via the REST API or JavaScript SDK. For a complete list of possible options, be sure to visit the technical reference for more information.

All the examples below expect a page with a configured instance of the Advanced Donation Form Part on it. It's also important the examples below are meant to be as simple and straight-forward as possible. In a real-world scenario, we highly suggestion following standard web practices for form design, including labels for accessibility, and client-side data validation.

For all the Advanced Donation Form examples, I've split the grouped the content by language - HTML, CSS, or JS; however, when using the code in the Advanced Donation Form Part, the code is combined into a single textarea. Please be certain to wrap the content with appropriate <script> or <style> tag.

Barebones Example

In our first example, we'll create a very barebones example of a donation form, supplying only the required fields. For this example we've included no styling or error handling, other than displaying a message in the console. I've also hard-coded the designation and amount fields.

Important The DonationService constructor requires the Advanced Donation Form Part ID. In the examples below, we dynamically find this ID. You could hard-code this value if you prefer. The technique below assumes there's only one Advanced Donation Form Part per page.

<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="first-name" /></dd>

  <dt>Last Name</dt>
  <dd><input type="text" id="last-name" /></dd>

  <dt>Email</dt>
  <dd><input type="text" id="email" /></dd>

  <dt>Address</dt>
  <dd><textarea id="address"></textarea></dd>

  <dt>City</dt>
  <dd><input type="text" id="city" /></dd>

  <dt>State</dt>
  <dd><input type="text" id="state" /></dd>

  <dt>Zip</dt>
  <dd><input type="text" id="zip" /></dd>

  <dt>Country</dt>
  <dd><input type="text" id="country" /></dd>
</dl>

<p><button class="btn-donate">Donate</button></p>
// Let's be good developers and not pollute the global namespace
(function($) {

  // Let's make sure the DOM is ready
  $(function() {

    // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );

    // Create the donation object we'll send
    // In order to simplify our examples, some of this information is hard-coded.
    var donation = {
      MerchantAccountId: '00000000-0000-0000-0000-000000000000',
      Gift: {
        PaymentMethod: 0,
        Designations: [
          {
            Amount: 5.00,
            DesignationId: '00000000-0000-0000-0000-000000000000'
          }
        ]
      }
    };

    // Create our success handler
    var success = function(returnedDonation) {
      console.log(returnedDonation);
    };

    // Create our error handler
    var error = function(returnedErrors) {
      console.log('Error!');
    };

    // Attach our event listener to the donate button
    $('.btn-donate').click(function(e) {

      // Stop the button from submitting the form
      e.preventDefault(); 

      // Add the information our user has typed
      donation.Donor = {
        FirstName: $('#first-name').val(),
        LastName: $('#last-name').val(),
        Address: {
          StreetAddress: $('#address').val(),
          City: $('#city').val(),
          State: $('#state').val(),
          PostalCode: $('#zip').val(),
          Country: $('#country').val()
        }
      };

      // Submit our donation
      ds.createDonation(donation, success, error);
    });

  });
}(jQuery));

Using the Payment 2.0 Part

When using the Payment 2.0 Part, the information required is reduced. For example, Donor information is should no longer be provided. Again, we're not including any styling or error handling, but I have introduced the ability to select a donation amount and designation.

<dl>
  <dt>Amount</dt>
  <dd>
    <select id="amount">
      <option value="5">5</option>
      <option value="10">10</option>
      <option value="20">20</option>
    </select>
  </dd>

  <dt>Designation</dt>
  <dd>
    <select id="designation">
      <option value="00000000-0000-0000-0000-000000000000">Example Designation 1</option>
      <option value="00000000-0000-0000-0000-000000000000">Example Designation 2</option>
    </select>
  </dd>
</dl>
      
<p><button class="btn-donate">Donate</button></p>
// Let's be good developers and not pollute the global namespace
(function($) {
  
  // Let's make sure the DOM is ready
  $(function() {
    
    // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );
    
    // Create the donation object we'll send
    // In order to simplify our examples, some of this information is hard-coded.
    var donation = {
      Gift: {
        PaymentMethod: 0,
        Designations: []
      }
    };
    
    // Create our success handler
    var success = function(returnedDonation) {
      console.log(returnedDonation);
    };
    
    // Create our error handler
    var error = function(returnedErrors) {
      console.log('Error!');
    };
    
    // Attach our event listener to the donate button
    $('.btn-donate').click(function(e) {
      
      // Stop the button from submitting the form
      e.preventDefault(); 
      
      // Add the information our user has typed
      donation.Gift.Designations.push({
        Amount: $('#amount').val(),
        DesignationId: $('#designation').val()
      });
      
      // Submit our donation
      ds.createDonation(donation, success, error);
    });
    
  });
}(jQuery));

Note The DonationService attempts to redirect to the BBSPCheckoutUri or PaymentPageUri if either of them exist (in that order) after a successful callback from the createDonation method.

Pledge Installments Example

Pledge installments are the payment donor makes for a pledge according to a defiend time schedule. The example includes the HTML required to display input fields to capture required parameters for pledge installment endpoints.

<fieldset class="donationInformation">
	<legend>
		<span class="BBListingHeading DonationListingHeading">Gift Information</span>
	</legend>
	<div>
		<input type="checkbox" id="isPaymentTwo"/> Check this box if you are using a Payment 2.0 part with this ADF
		<div class="giftAmount">
			<label class="required">Select Gift Amount:</label>
			<ul>
				<li>
					<input id="amt1000" type="radio" name="radioAmount" value="1000" />
					<label for="amt1000">
						<span class="term">Platinum</span>
						<span class="value">$1,000</span>
					</label>
				</li>
				<li>
					<input id="amt500" type="radio" name="radioAmount" value="500" />
					<label for="amt500">
						<span class="term">Gold</span>
						<span class="value">$500</span>
					</label>
				</li>
				<li>
					<input id="amt100" type="radio" name="radioAmount" value="100" />
					<label for="amt100">
						<span class="term">Silver</span>
						<span class="value">$100</span>
					</label>
				</li>
				<li>
					<input id="amt25" type="radio" name="radioAmount" value="25" />
					<label for="amt25">
						<span class="term">Bronze</span>
						<span class="value">$25</span>
					</label>
				</li>
				<li class="other">
					<input id="amtOther" type="radio" name="radioAmount" value="-1" checked="checked" />
					<label for="amtOther">
						<span class="term">Enter an amount</span>
					</label>
					<input id="txtAmount" type="text" placeholder="$" class="BBFormTextbox">
				</li>
			</ul>
		</div>
	</div>
</fieldset>
        
<!-- PLEDGE INSTALLMENT START-->
<div class="pledgeInstallments">
	<label class="required">Pledge Installment:</label>
	<ul>
		<li class="other">
			<label for="numberOfInstallments">
				Number of Installments:
			</label>
			<input id="numberOfInstallments" type="text" placeholder="$" class="BBFormTextbox">
		</li>
		<li class="other">
			Installment Amount:
			<label id="installmentAmount"/>
		</li>
		<li class="other">
			Installment End Date:
			<label id="installmentEndDate" />
		</li>
	</ul>
</div>
	<!-- PLEDGE INSTALLMENT END-->
// Let's be good developers and not pollute the global namespace
(function($) {
  
  // Let's make sure the DOM is ready
  $(function() {
    
    // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );
    
    // Create the donation object we'll send
    // In order to simplify our examples, some of this information is hard-coded.
    var donation = {
      MerchantAccountId: '00000000-0000-0000-0000-000000000000',
      Gift: {
        PaymentMethod: 0,
        Designations: [
          {
		  //Amount has been hardcoded to 500. Replace the value with a value entered by user.
            Amount: 500.00,
            DesignationId: '00000000-0000-0000-0000-000000000000'
          }
        ]
      }
    };
    
    // Create our success handler
    var success = function(returnedDonation) {
      console.log(returnedDonation);
    };
    
    // Create our error handler
    var error = function(returnedErrors) {
      console.log('Error!');
    };
    
    // Attach our event listener to the donate button
    $('.btn-donate').click(function(e) {
      
      // Stop the button from submitting the form
      e.preventDefault();
	  
      //if payment 2.0 is checked, No need to capture the donor information
       if(!isPaymentTwo){
		  donation.Donor = {
			Title: $("#title").val(),
			FirstName: $("#firstName").val(),
			LastName: $("#lastName").val(),
			EmailAddress: $("#emailAddress").val(),
			Phone: $("#phone").val(),
			Address: {
			  Country: $("#country").val(),
			  State: $("#state").val(),
			  City: $("#city").val(),
			  StreetAddress: $("#streetAddress").val(),
			  PostalCode: $("#postalCode").val()
			},
			OrganizationName: organizationName
		  };
	  }
	  
	   // PLEDGE INSTALLMENT BEGIN ********************************************************************************************************************************************************************************************************************************************************
	  var numberOfInstallments = $('#numberOfInstallments').val();
	  if (numberOfInstallments) {
	      //Amount has been hardcoded to 500. Replace the value with a value entered by user.
		  var installmentAmount = ds.getRecurringGiftInstallmentAmount(500, numberOfInstallments);
		  donation.Gift.PledgeInstallment = {
			  NumberOfInstallments: numberOfInstallments,
			  InstallmentAmount: installmentAmount
		  }
	  }
      // PLEDGE INSTALLMENT END ********************************************************************************************************************************************************************************************************************************************************
        
      // Submit our donation
      ds.createDonation(donation, success, error);
    });
    
  });
}(jQuery));

Displaying Available Countries and States

Using the Country and State endpoints, you can quickliy populate the available countries and their corresponding states. The first example uses jQuery to make an AJAX request. We also wrap this functionality in the Country and State JavaScript Service, which is shown in the second example.

<dl>
  <dt>Country</dt>
  <dd><select id="country"></select></dd>
  
  <dt>State</dt>
  <dd><select id="state"></select></dd>
</dl>
// Let's be good developers and not pollute the global namespace
(function($) {

// Let's make sure the DOM is ready
$(function() {

  var selectCountry = $('#country');
  var selectState = $('#state');
  
  // Load Countries
  $.get(BLACKBAUD.api.pageInformation.rootPath + '/webapi/country', function(countries) {
    for (var i = 0, j = countries.length; i < j; i++) {
      selectCountry.append('<option value="' + countries[i].Id + '">' + countries[i].Description + '</option>');
    }
  });
  
  // Watch Country Change
  $('#country').on('change', function() {
  
    // Load States
    $.get(BLACKBAUD.api.pageInformation.rootPath + '/webapi/country/' + $(this).val() + '/state', function(states) {
      selectState.html('');
      for (var i = 0, j = states.length; i < j; i++) {
        selectState.append('<option value="' + states[i].Id + '">' + states[i].Description + '</option>');
      }
    });
  });

});
}(jQuery));

The following example uses the Country and State JavaScript Service instead of making an AJAX call directly to the endpoints.

<dl>
  <dt>Country</dt>
  <dd><select id="country"></select></dd>
  
  <dt>State</dt>
  <dd><select id="state"></select></dd>
</dl>
// Let's be good developers and not pollute the global namespace
(function($) {

// Let's make sure the DOM is ready
$(function() {

  var selectCountry = $('#country');
  var selectState = $('#state');
  var service = new BLACKBAUD.api.CountryService();
  
  // Load Countries
  service.getCountries(function(countries) {
    for (var i = 0, j = countries.length; i < j; i++) {
      selectCountry.append('<option value="' + countries[i].Id + '">' + countries[i].Description + '</option>');
    }
  });
  
  // Watch Countries Change
  $('.countries').on('change', function() {
  
    // Load States
    service.getStates($(this).val(), function(states) {
      selectState.html('');
      for (var i = 0, j = states.length; i < j; i++) {
        selectState.append('<option value="' + states[i].Id + '">' + states[i].Description + '</option>');
      }
    });
  });

});
}(jQuery));

Recurrence Example

In this example, we're back to "proceeding directly to payment," as opposed to using the Payment 2.0 Part. This means we'll need to collect the donor information.

Using recurrence with the Advanced Donation Form can be a little tricky, in that the fields which are required, differs depending on the frequency of the recurrence. For example, the Month field is only valid and required if the Frequency field is set to Annually (4).

<h3>Donor Information</h3>
<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="first-name" /></dd>
  
  <dt>Last Name</dt>
  <dd><input type="text" id="last-name" /></dd>
  
  <dt>Email</dt>
  <dd><input type="text" id="email" /></dd>
  
  <dt>Address</dt>
  <dd><textarea id="address"></textarea></dd>
  
  <dt>City</dt>
  <dd><input type="text" id="city" /></dd>
  
  <dt>State</dt>
  <dd><input type="text" id="state" /></dd>
  
  <dt>Zip</dt>
  <dd><input type="text" id="zip" /></dd>
  
  <dt>Country</dt>
  <dd><input type="text" id="country" /></dd>
</dl>

<h2>Recurrence</h2>
<dl>
  <dt>Frequency</dt>
  <dd>
    <select id="frequency">
      <option value="1">Weekly</option>
      <option value="2">Monthly</option>
      <option value="3">Quarterly</option>
      <option value="4">Annually</option>
    </select>
  </dd>
  
  <dt>Start Date</dt>
  <dd><input type="text" id="start-date" /></dd>
  
  <dt>Day of Week</dt>
  <dd>
    <select id="day-of-week">
      <option value="0">Sunday</option>
      <option value="1">Monday</option>
      <option value="2">Tuesday</option>
      <option value="3">Wednesday</option>
      <option value="4">Thursday</option>
      <option value="5">Friday</option>
      <option value="6">Saturday</option>
    </select>
  </dd>
  
  <dt>Day of Month</dt>
  <dd>
    <select id="day-of-month">
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option>
      <option>5</option>
      <option>6</option>
      <option>7</option>
      <option>8</option>
      <option>9</option>
      <option>10</option>
      <option>11</option>
      <option>12</option>
      <option>13</option>
      <option>14</option>
      <option>15</option>
      <option>16</option>
      <option>17</option>
      <option>18</option>
      <option>19</option>
      <option>20</option>
      <option>21</option>
      <option>22</option>
      <option>23</option>
      <option>24</option>
      <option>25</option>
      <option>26</option>
      <option>27</option>
      <option>28</option>
      <option>29</option>
      <option>30</option>
      <option>31</option>
    </select>
  </dd>
</dl>

<p><button class="btn-donate">Donate</button></p>
// Let's be good developers and not pollute the global namespace
(function($) {
  
  // Let's make sure the DOM is ready
  $(function() {
    
    // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );
    
    // Create the donation object we'll send
    // In order to simplify our examples, some of this information is hard-coded.
    var donation = {
      MerchantAccountId: '00000000-0000-0000-0000-000000000000',
      Gift: {
        PaymentMethod: 0,
        Designations: [
          {
            Amount: 5.00,
            DesignationId: '00000000-0000-0000-0000-000000000000'
          }
        ]
      }
    };
    
    // Create our success handler
    var success = function(returnedDonation) {
      console.log(returnedDonation);
    };
    
    // Create our error handler
    var error = function(returnedErrors) {
      console.log('Error!');
    };
    
    // Attach our event listener to the donate button
    $('.btn-donate').click(function(e) {
      
      // Stop the button from submitting the form
      e.preventDefault(); 
      
      // Add the information our user has typed
      donation.Donor = {
        FirstName: $('#first-name').val(),
        LastName: $('#last-name').val(),
        Address: {
          StreetAddress: $('#address').val(),
          City: $('#city').val(),
          State: $('#state').val(),
          PostalCode: $('#zip').val(),
          Country: $('#country').val()
        }
      };
      
      // The following fields are always required
      donation.Gift.Recurrence = {
        Frequency: $('#frequency').val(),
        StartDate: $('#start-date').val()
      };
      
      // The remaining required values are different depending on what frequency the user selects
      var frequency = $('#frequency').val();
      switch (frequency) {
        case 1:
          donation.Gift.Recurrence.DayOfWeek = $('#day-of-week').val();
        break;
        case 2:
        case 3:
          donation.Gift.Recurrence.DayOfMonth = $('#day-of-month').val();
          // Purposefully letting selection fall through (no "break")
        case 4:
          donation.Gift.Recurrence.Month = $('#month').val();
        break;
      };
        
      // Submit our donation
      ds.createDonation(donation, success, error);
    });
    
  });
}(jQuery));

Tribute Example

In this example, we're back to "proceeding directly to payment," as opposed to using the Payment 2.0 Part. This means we'll need to collect the donor information. I've also introduced the ability to add a tribute.

<h3>Donor Information</h3>
<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="first-name" /></dd>
  
  <dt>Last Name</dt>
  <dd><input type="text" id="last-name" /></dd>
  
  <dt>Email</dt>
  <dd><input type="text" id="email" /></dd>
  
  <dt>Address</dt>
  <dd><textarea id="address"></textarea></dd>
  
  <dt>City</dt>
  <dd><input type="text" id="city" /></dd>
  
  <dt>State</dt>
  <dd><input type="text" id="state" /></dd>
  
  <dt>Zip</dt>
  <dd><input type="text" id="zip" /></dd>
  
  <dt>Country</dt>
  <dd><input type="text" id="country" /></dd>
</dl>

<h3>Tribute Information</h3>
<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="tribute-first-name" /></dd>
  
  <dt>Last Name</dt>
  <dd><input type="text" id="tribute-last-name" /></dd>
  
  <dt>Description</dt>
  <dd><textarea id="tribute-description"></textarea></dd>
</dl>
      
<h3>Acknowledgee Information</h3>
<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="acknowledgee-first-name" /></dd>
  
  <dt>Last Name</dt>
  <dd><input type="text" id="acknowledgee-last-name" /></dd>
  
  <dt>Email</dt>
  <dd><input type="text" id="acknowledgee-email" /></dd>
  
  <dt>Address</dt>
  <dd><textarea id="acknowledgee-address"></textarea></dd>
  
  <dt>City</dt>
  <dd><input type="text" id="acknowledgee-city" /></dd>
  
  <dt>State</dt>
  <dd><input type="text" id="acknowledgee-state" /></dd>
  
  <dt>Zip</dt>
  <dd><input type="text" id="acknowledgee-zip" /></dd>
  
  <dt>Country</dt>
  <dd><input type="text" id="acknowledgee-country" /></dd>
</dl>
      
<p><button class="btn-donate">Donate</button></p>
// Let's be good developers and not pollute the global namespace
(function($) {
  
  // Let's make sure the DOM is ready
  $(function() {
    
    // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );
    
    // Create the donation object we'll send
    // In order to simplify our examples, some of this information is hard-coded.
    var donation = {
      MerchantAccountId: '00000000-0000-0000-0000-000000000000',
      Tribute: {},
      Gift: {
        PaymentMethod: 0,
        Designations: [
          {
            Amount: 5.00,
            DesignationId: '00000000-0000-0000-0000-000000000000'
          }
        ]
      }
    };
    
    // Create our success handler
    var success = function(returnedDonation) {
      console.log(returnedDonation);
    };
    
    // Create our error handler
    var error = function(returnedErrors) {
      console.log('Error!');
    };
    
    // Attach our event listener to the donate button
    $('.btn-donate').click(function(e) {
      
      // Stop the button from submitting the form
      e.preventDefault(); 
      
      // Add the information our user has typed
      donation.Donor = {
        FirstName: $('#first-name').val(),
        LastName: $('#last-name').val(),
        Address: {
          StreetAddress: $('#address').val(),
          City: $('#city').val(),
          State: $('#state').val(),
          PostalCode: $('#zip').val(),
          Country: $('#country').val()
        }
      };

      // Add our tribute information
      // See REST api tech reference for more information on the Type field
      donation.Tribute.TributeDefintion = {
        Type: 'Tribute',      
        FirstName: $('#tribute-first-name'),
        LastName: $('#tribute-last-name'),
        Description: $('tribute-description')
      };
      
      // Add our acknowledgee information
      donation.Tribute.Acknowledgee = {
        FirstName: $('#acknowledgee-first-name').val(),
        LastName: $('#acknowledgee-last-name').val(),
        Address: {
          StreetAddress: $('#acknowledgee-address').val(),
          City: $('#acknowledgee-city').val(),
          State: $('#acknowledgee-state').val(),
          PostalCode: $('#acknowledgee-zip').val(),
          Country: $('#acknowledgee-country').val()
        }
      };
      
      // Submit our donation
      ds.createDonation(donation, success, error);
    });
    
  });
}(jQuery));

Using Blackbaud Secure Payments

Integration with the Blackbaud Secure Payments service has been updated in the Advanced Donation Form.

<dl>
  <dt>First Name</dt>
  <dd><input type="text" id="first-name" /></dd>

  <dt>Last Name</dt>
  <dd><input type="text" id="last-name" /></dd>

  <dt>Email</dt>
  <dd><input type="text" id="email" /></dd>

  <dt>Address</dt>
  <dd><textarea id="address"></textarea></dd>

  <dt>City</dt>
  <dd><input type="text" id="city" /></dd>

  <dt>State</dt>
  <dd><input type="text" id="state" /></dd>

  <dt>Zip</dt>
  <dd><input type="text" id="zip" /></dd>

  <dt>Country</dt>
  <dd><input type="text" id="country" /></dd>
</dl>

<p><button class="btn-donate">Donate</button></p>
// Let's be good developers and not pollute the global namespace
(function($) {

// Let's make sure the DOM is ready
$(function() {

  // Create an instance of the DonationService
    var ds = new BLACKBAUD.api.DonationService(
      $('.BBDonationApiContainer').data('partid')
    );

  // Create the donation object we'll send
  // In order to simplify our examples, some of this information is hard-coded.
  var donation = {
    MerchantAccountId: '00000000-0000-0000-0000-000000000000',
    BBSPReturnUri: window.location.href,
    BBSPTemplateSitePageId: 000,
    Gift: {
      PaymentMethod: 0,
      Designations: [
        {
          Amount: 5.00,
          DesignationId: '00000000-0000-0000-0000-000000000000'
        }
      ]
    }
  };

  // Create our success handler
  var success = function(returnedDonation) {
    console.log(returnedDonation);
  };

  // Create our error handler
  var error = function(returnedErrors) {
    console.log('Error!');
  };

  // Attach our event listener to the donate button
  $('.btn-donate').click(function(e) {

    // Stop the button from submitting the form
    e.preventDefault(); 

    // Add the information our user has typed
    donation.Donor = {
      FirstName: $('#first-name').val(),
      LastName: $('#last-name').val(),
      Address: {
        StreetAddress: $('#address').val(),
        City: $('#city').val(),
        State: $('#state').val(),
        PostalCode: $('#zip').val(),
        Country: $('#country').val()
      }
    };

    // Submit our donation
    ds.createDonation(donation, success, error);
  });

});
}(jQuery));

Styling + Error Handling + API Integration

This last example is meant to show a more realistic real-world example of the Advanced Donation Form API, including all the necessary error handling and styling. This example also incorporates the use of the CountryService, which is the javascript wrapper for the Country REST API endpoint.

Whereas all the other examples are designed to be inserted into an Advanced Donation Form Part, the complete example is designed to be completely standalone - even on a different server than the one serving your BBIS website.

Please note you'll need to update the references in the example below to your BBIS installation.

<!DOCTYPE html>
<html lang="en" ng-app="bbADF">
<head>
  
  <title>ADF - Complete Example</title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css">
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
  <link rel="stylesheet" href="example-complete.css">
    
</head>
<body>
  
  <form class="form-horizontal" ng-controller="DonationController">
    <div class="container">
      <div class="row">
        <div class="col-sm-12">
          <h1>Advanced Donation Form <small>Complete Example</small></h1>
        </div>
      </div>
      <div class="row">
        <div class="col-sm-6">
          
          <h3>Donor Information</h3>
          
          <div class="form-group">
            <div class="col-sm-6">
              <input type="text" class="form-control" ng-model="Donor.Firstname" placeholder="Firstname" required>
            </div>
            <div class="col-sm-6">
              <input type="text" class="form-control" ng-model="Donor.Lastname" placeholder="Lastname" required>
            </div>
          </div>

          <div class="form-group">
            <div class="col-sm-12">
              <textarea class="form-control" placeholder="Address" ng-model="Donor.Address.StreetAddress"></textarea>
            </div>
          </div>
          
          <div class="form-group">
            <div class="col-sm-6">
              <select class="form-control" ng-model="Donor.Address.Country" ng-change="getStates()" ng-options="c.Description for c in countries track by c.Id" ng-change="getStates()" required>
                <option value="">Country</option>
              </select>
            </div>
            <div class="col-sm-6">
              <select class="form-control" ng-model="Donor.Address.State" ng-options="s.Description for s in states track by s.Id" required>
                <option value="">State</option>
              </select>
            </div>
          </div>

          <div class="form-group">
            <div class="col-sm-9">
              <input type="text" class="form-control" placeholder="City" ng-model="Donor.Address.City">
            </div>
            <div class="col-sm-3">
              <input type="text" class="form-control" placeholder="Zip" ng-model="Donor.Address.PostalCode" />
            </div>
          </div>

          <div class="form-group">
            <div class="col-sm-8">
              <input type="text" class="form-control" placeholder="Email" ng-model="Donor.EmailAddress" />
            </div>
            <div class="col-sm-4">
              <input type="text" class="form-control" placeholder="Phone" ng-model="Donor.Phone" />
            </div>
          </div>
          
          <div class="form-group">
            <div class="col-sm-4">
              <label class="btn btn-default" ng-model="Gift.IsCorporate" btn-checkbox>
                <i class="fa fa-fw" ng-class="{ 'fa-square-o': !Gift.IsCorporate, 'fa-check-square-o': Gift.IsCorporate }"></i> Corporate?
              </label>
            </div>
            <div class="col-sm-8" ng-show="Gift.IsCorporate">
              <input type="text" class="form-control" ng-model="Donor.OrganizationName" placeholder="Organization Name" required>
            </div>
          </div>
          
          <div class="form-group">
            <div class="col-sm-4">
              <label class="btn btn-default" ng-model="Gift.isAnonymous" btn-checkbox>
                <i class="fa fa-fw" ng-class="{ 'fa-square-o': !Gift.isAnonymous, 'fa-check-square-o': Gift.isAnonymous }"></i> Anonymous?
              </label>
            </div>
          </div>
          
        </div>
        <div class="col-sm-6">
        
          <h3>Gift Information</h3>
          
          <div class="form-group">
            <div class="col-sm-6">
              <div class="btn-group">
                <label class="btn btn-default" ng-repeat="option in amounts" ng-model="defaults.amount" btn-radio="option.amount" required>
                  <span ng-bind="option.label"></span>
                </label>
              </div>
            </div>
            <div class="col-sm-6">
              <div class="btn-group">
                <label class="btn btn-default" ng-repeat="option in payments" ng-model="defaults.payments" btn-radio="option.id" required>
                  <span ng-bind="option.label"></span>
                </label>
              </div>
            </div>
          </div>
          
          <div class="form-group" ng-show="defaults.amount == 'other'">
            <div class="col-sm-12">
              <div class="input-group">
                <div class="input-group-addon">$</div>
                <input type="text" class="form-control" ng-model="Gift.Amount" placeholder="Other Amount" ng-required="Gift.SelectedAmount == 'other'">
              </div>
            </div>
          </div>
          
          <div class="form-group">
            <div class="col-sm-12">
              <textarea class="form-control" ng-model="Gift.Comments" placeholder="Comments"></textarea>
            </div>
          </div>
          
          <div class="form-group">
            <div class="col-sm-6">
              <select class="form-control" ng-model="defaults.designation" ng-options="designation.label for designation in designations track by designation.id" required>
                <option value="">Designation</option>
              </select>
            </div>
            <div class="col-sm-6">
              <div class="btn-group">
                <label class="btn btn-default" ng-repeat="option in types" ng-model="defaults.type" btn-radio="option.id">
                  <span ng-bind="option.label"></span>
                </label>
              </div>
            </div>
          </div>
          
          <div ng-show="defaults.type == 'recurring'">

            <div class="form-group">
              <div class="col-sm-6">
                <div class="input-group">
                  <input type="text" class="form-control" ng-model="Gift.Recurrence.StartDate" datepicker-popup="MMMM dd, yyyy" is-open="openedStartDate" show-button-bar="false" min-date="minDate" placeholder="Start Date" required />
                  <span class="input-group-btn">
                    <button type="button" class="btn btn-default" ng-click="open($event, 'openedStartDate')">
                      <i class="fa fa-calendar"></i>
                    </button>
                  </span>
                </div>
              </div>
              <div class="col-sm-6">
                <div class="input-group">
                  <input type="text" class="form-control" ng-model="Gift.Recurrence.EndDate" datepicker-popup="MMMM dd, yyyy" is-open="openedEndDate" show-button-bar="false" min-date="minDate" placeholder="End Date" />
                  <span class="input-group-btn">
                    <button type="button" class="btn btn-default" ng-click="open($event, 'openedEndDate')">
                      <i class="fa fa-calendar"></i>
                    </button>
                  </span>
                </div>
              </div>
            </div>
            
            <div class="form-group">
              <div class="col-sm-4">
                <select class="form-control" ng-model="Gift.Recurrence.Frequency" required>
                  <option value="">Frequency</option>
                  <option value="1">Weekly</option>
                  <option value="2">Monthly</option>
                  <option value="3">Quarterly</option>
                  <option value="4">Annually</option>
                </select>
              </div>
              <div ng-show="Gift.Recurrence.Frequency">
                <div class="col-sm-4" ng-show="Gift.Recurrence.Frequency == 1">
                  <select class="form-control" ng-model="Gift.Recurrence.Month" required>
                    <option value="">Month</option>
                    <option value="1">January</option>
                    <option value="2">February</option>
                    <option value="3">March</option>
                    <option value="4">April</option>
                    <option value="5">May</option>
                    <option value="6">June</option>
                    <option value="7">July</option>
                    <option value="8">August</option>
                    <option value="9">September</option>
                    <option value="10">October</option>
                    <option value="11">November</option>
                    <option value="12">December</option>
                  </select>
                </div>
                <div class="col-sm-4" ng-show="Gift.Recurrence.Frequency == 1">
                  <select class="form-control" ng-model="Gift.Recurrence.DayOfWeek" required>
                    <option value="">Day of Week</option>
                    <option value="0">Sunday</option>
                    <option value="1">Monday</option>
                    <option value="2">Tuesday</option>
                    <option value="3">Wednesday</option>
                    <option value="4">Thursday</option>
                    <option value="5">Friday</option>
                    <option value="6">Saturday</option>
                  </select>
                </div>
                <div class="col-sm-4" ng-show="Gift.Recurrence.Frequency != 1">
                  <select class="form-control" ng-model="Gift.Reccurence.DayOfMonth" required>
                    <option value="">Day of Month</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                    <option value="6">6</option>
                    <option value="7">7</option>
                    <option value="8">8</option>
                    <option value="9">9</option>
                    <option value="10">10</option>
                    <option value="11">11</option>
                    <option value="12">12</option>
                    <option value="13">13</option>
                    <option value="14">14</option>
                    <option value="15">15</option>
                    <option value="16">16</option>
                    <option value="17">17</option>
                    <option value="18">18</option>
                    <option value="19">19</option>
                    <option value="20">20</option>
                    <option value="21">21</option>
                    <option value="22">22</option>
                    <option value="23">23</option>
                    <option value="24">24</option>
                    <option value="25">25</option>
                    <option value="26">26</option>
                    <option value="27">27</option>
                    <option value="28">28</option>
                    <option value="29">29</option>
                    <option value="30">30</option>
                    <option value="31">31</option>
                  </select>
                </div>
              </div>
            </div>

          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-sm-6">

          <div class="form-group">
            <div class="col-sm-12">
              
            </div>
          </div>
        </div>
        <div class="col-sm-6">
          
        </div>
      </div>
      <div class="row">
        <div class="col-sm-12">
          <p>
            <button class="btn btn-lg btn-primary btn-donate" ng-click="log()">
              <i class="fa fa-lock"></i> Donate
            </button>
          </p>
        </div>
      </div>
    </div>
  
  </form>
  
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.11.2/ui-bootstrap-tpls.min.js"></script>
  <script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
  <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>  
  <script src="//chs6bobbyear02.blackbaud.global/Client/Scripts/easyXDM/easyXDM.min.js"></script>
  <script src="//chs6bobbyear02.blackbaud.global/Client/Scripts/API/BBAPI-min.js"></script>
  <script src="example-complete.js"></script>
  
</body>
</html>
'use strict';

// Create our Angular application
angular.module('bbADF', ['ui.bootstrap'])
.controller('DonationController', function($scope, CountryService) {

  // Controls the available donor types
  $scope.defaults = {
    type: '', // single | recurring
    payments: '', // credit | later
    amount: 0 // dollar amount | other
  };

  $scope.types = [
    {
      id: 'single',
      label: 'Single Gift'
    },
    {
      id: 'recurring',
      label: 'Recurring Gift'
    }
  ];

  $scope.payments = [
    {
      id: 'credit',
      label: 'Credit Card'
    },
    {
      id: 'later',
      label: 'Bill Me Later'
    }
  ];

  $scope.amounts = [
    {
      amount: 5,
      label: '$5'
    },
    {
      amount: 10,
      label: '$10'
    },
    {
      amount: 25,
      label: '$25'
    },
    {
      amount: 'other',
      label: 'Other'
    }
  ];

  $scope.designations = [
    {
      id: '0001',
      label: 'Designation 1'
    },
    {
      id: '0002',
      label: 'Designation 2'
    }
  ];

  // Calendar manipulation
  $scope.minDate = new Date();
  $scope.open = function($event, opened) {
    $event.preventDefault();
    $event.stopPropagation();
    $scope[opened] = true;
  };

  // Temporarily debugging
  $scope.log = function() {
    console.log(this);
  };
  
  // Request the states for a particular country
  $scope.getStates = function() {
    CountryService.getStates($scope.Donor.Address.Country.Id).success(function(data) {
      $scope.states = data;
    }).error(function() {
      alert('Error loading states.');
    });
  }

  CountryService.getCountries().success(function(data) {
    $scope.countries = data;
  }).error(function() {
    alert('Error loading countries.');
  });

})
.service('CountryService', function($http) {
  
  // Baseurl when using the Advanced Donation Form API
  var baseurl = 'http://chs6bobbyear02.blackbaud.global/Webapi/';

  // Request countries
  this.getCountries = function() {
    return $http.get(baseurl + 'Country');
  }

  // Request states when a country is selected
  this.getStates = function(id) {
    return $http.get(baseurl + 'Country/' + id + '/State');
  };

});
/* Currently only using Bootstrap styles */

Advanced Donation Form Generator

Overview:

The ADF form generator is a custom content part to be used as an internal tool to generate a barebones ADF template HTML document that developers can use as a starting point for their ADF work. The workflow for using the generator is outlined below.

  1. Create a new custom content part using the Administration menu in BBIS.
  2. Copy and paste the "Editor" and "Web page display" HTML code available on Github.
  3. In the initialize javascript function field, put "init_form" as the function name.
  4. Fill out the name and description to your liking.
  5. Save the content part.
  6. Create a new page from Site Explorer. It should be hidden from the public facing site, as it will only be used to host the generator tool for internal access.
  7. On the newly created internal page, insert the custom content part created above. Use the edit feature to fill in the options you would like your form to have/not have. Remember to populat your designations list.
  8. Select save and the form will be updated with your selections
  9. Select "View This Page" in the top left corner of the BBIS site, and then click the "Get HTML" button on the donation form. A modal will appear from which you can copy the code for your own ADF.
  10. Paste the code into your favorite text editor. There are some "TODO" tags throughout the code that you must act on prior to the ADF going live. Most just require you to delete leftover code from the generator. Use your editor's "find" feature (Ctrl + F) to find all strings matching "TODO" to make sure you see them all.
  11. Uncomment and fill out the data in the first javascript function. Follow the example format to ensure it works correctly. GUID's can be found using the special selectors or querying the appropriate database tables.
  12. Copy and paste the completed code to an ADF part. You now have a basic, fully functioning, donation form up and running.

Known Issues:

  • Tributes need tribute definition details / types in order for them to work correctly. This was not implemented in the wizard. In order for tributes to function correctly, you will need to populate the relevant properties yourself. See the ADF "Create Donation" API for more information.
  • Inputted details such as merchant account ID and BBSPReturnUri do not carry over to the form options. Instead, manually enter the information as outlined in step 11.

The entirety of this code is publicly available on Github. Community input and pull requests are welcome and appreciated.