AngularJS Contact Form with Bootstrap and PHPMailer

The form is styled with Bootstrap. Validation and sending the form data to the server is done with AngularJS. More validation and processing the email is done with PHPMailer on the server.


In index.html setup the form and add all the Angular directives:

<!DOCTYPE html>
    <title>AngularJS Contact Form with Bootstrap and PHPMailer</title>
    <link rel="stylesheet" href="">
    <link rel="stylesheet" href="">
    <link rel="stylesheet" href="style.css">
    <script src=""></script>
    <script src=""></script>
    <script src=""></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
<body ng-app="contactApp" >
    <div class="vertical-middle">
        <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h2 class="panel-title">Contact Form</h2>
                <div ng-controller="ContactController" class="panel-body">
                    <form ng-submit="submit(contactform)" name="contactform" method="post" action="" class="form-horizontal" role="form">
                        <div class="form-group" ng-class="{ 'has-error': contactform.inputName.$invalid && submitted }">
                            <label for="inputName" class="col-lg-2 control-label">Name</label>
                            <div class="col-lg-10">
                                <input ng-model="formData.inputName" type="text" class="form-control" id="inputName" name="inputName" placeholder="Your Name" required>
                        <div class="form-group" ng-class="{ 'has-error': contactform.inputEmail.$invalid && submitted }">
                            <label for="inputEmail" class="col-lg-2 control-label">Email</label>
                            <div class="col-lg-10">
                                <input ng-model="formData.inputEmail" type="email" class="form-control" id="inputEmail" name="inputEmail" placeholder="Your Email" required>
                        <div class="form-group" ng-class="{ 'has-error': contactform.inputSubject.$invalid && submitted }">
                            <label for="inputSubject" class="col-lg-2 control-label">Subject</label>
                            <div class="col-lg-10">
                                <input ng-model="formData.inputSubject" type="text" class="form-control" id="inputSubject" name="inputSubject" placeholder="Subject Message" required>
                        <div class="form-group" ng-class="{ 'has-error': contactform.inputMessage.$invalid && submitted }">
                            <label for="inputMessage" class="col-lg-2 control-label">Message</label>
                            <div class="col-lg-10">
                                <textarea ng-model="formData.inputMessage" class="form-control" rows="4" id="inputMessage" name="inputMessage" placeholder="Your message..." required></textarea>
                        <div class="form-group">
                            <div class="col-lg-offset-2 col-lg-10">
                                <button type="submit" class="btn btn-default" ng-disabled="submitButtonDisabled">
                                    Send Message
                    <p ng-class="result" style="padding: 15px; margin: 0;">{{ resultMessage }}</p>

Assign a variable to the Angular app in app.js:

var app = angular.module('contactApp', []);

Add a controller to the app for processing the form. Form data is sent via ajax to contact-form.php using Angular’s $http service:

app.controller('ContactController', function ($scope, $http) {
    $scope.result = 'hidden'
    $scope.formData; //formData is an object holding the name, email, subject, and message
    $scope.submitButtonDisabled = false;
    $scope.submitted = false; //used so that form errors are shown only after the form has been submitted
    $scope.submit = function(contactform) {
        $scope.submitted = true;
        $scope.submitButtonDisabled = true;
        if (contactform.$valid) {
                method  : 'POST',
                url     : 'contact-form.php',
                data    : $.param($scope.formData),  //param method from jQuery
                headers : { 'Content-Type': 'application/x-www-form-urlencoded' }  //set the headers so angular passing info as form data (not request payload)
                if (data.success) { //success comes from the return json object
                    $scope.submitButtonDisabled = true;
                    $scope.resultMessage = data.message;
                } else {
                    $scope.submitButtonDisabled = false;
                    $scope.resultMessage = data.message;
        } else {
            $scope.submitButtonDisabled = false;
            $scope.resultMessage = 'Failed :(  Please fill out all the fields.';

The actual emailing is done in contact-form.php on the server. Here some additional validation is done to ensure the form has been filled out. Then an instance of PHPMailer is created and the mail is sent. The page sends back a success or failure message to the Angular $http service:

require_once 'phpmailer/PHPMailerAutoload.php';

if (isset($_POST['inputName']) && isset($_POST['inputEmail']) && isset($_POST['inputSubject']) && isset($_POST['inputMessage'])) {

    //check if any of the inputs are empty
    if (empty($_POST['inputName']) || empty($_POST['inputEmail']) || empty($_POST['inputSubject']) || empty($_POST['inputMessage'])) {
        $data = array('success' => false, 'message' => 'Please fill out the form completely.');
        echo json_encode($data);

    //create an instance of PHPMailer
    $mail = new PHPMailer();

    $mail->From = $_POST['inputEmail'];
    $mail->FromName = $_POST['inputName'];
    $mail->AddAddress(''); //recipient 
    $mail->Subject = $_POST['inputSubject'];
    $mail->Body = "Name: " . $_POST['inputName'] . "\r\n\r\nMessage: " . stripslashes($_POST['inputMessage']);

    if (isset($_POST['ref'])) {
        $mail->Body .= "\r\n\r\nRef: " . $_POST['ref'];

    if(!$mail->send()) {
        $data = array('success' => false, 'message' => 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo);
        echo json_encode($data);

    $data = array('success' => true, 'message' => 'Thanks! We have received your message.');
    echo json_encode($data);

} else {

    $data = array('success' => false, 'message' => 'Please fill out the form completely.');
    echo json_encode($data);


View a demo here. Download the source files.

- Submitting AJAX Forms: The AngularJS Way
- AngularJS ngClass
- AngularJS ngDisabled
- About $valid and $invalid
- I got the Bootstrap form from a good blog, but I forgot to bookmark it. I’ll add the link when I find it again.

This entry was posted in Uncategorized. Bookmark the permalink.
  • jzgdev

    I tried implementing this in my project and I got a 404 not found; so then I tried it directly from source and I still get a 404 error…. any ideas?

  • jzgdev

    I realized my problem was a server issue, any chance you could give some insight into your server set up? I have it configured right now with php requests proxied to nginx through express and it seems ok, I get a 200 message but can’t seem to get any result other than

    {“success”:false,”message”:”Please fill out the form completely.”}

  • 10chars

    Thanks for this, exactly what I was after – saved me hours of messing around!

  • Eddie Adolf

    @jzgdev:disqus I think the solution is to add the property novalidate in the form tag. This forces the angularjs validation and disables the html5 validation. Let me know if it works.

  • Patrick Cummings

    I was curious. Can you add file upload to this implementation fairly easily?

  • digitallegend

    There is a way to remove jquery completly?

    • lawless99

      Add this function to your controller

      var param = function(data) {

      var returnString = ”;

      for (var d in data){

      if (data.hasOwnProperty(d))

      returnString += d + ‘=’ + data[d] + ‘&’;


      return returnString.slice( 0, returnString.length – 1 );


      and replace ‘$.param($scope.formData)’ with ‘param($scope.formData)’

  • lawless99

    Great tut, thank you very much! I did however notice that the page refreshes when submitting. I managed to find two solutions:

    Option 1 (least favorable)
    - Remove action=”” from the form tag

    Option 2
    - Update ng-submit to ng-submit=”submit(contactform, $event)” to include the $event
    - Update controller submit function to:

    $scope.submit = function(contactform, e) {

    Hope this helps, thanks again for this :)

    • antonio ortiz

      Hey lawless99,

      I tried that but it seemed to just refresh just like you mentioned! Have you gotten it to work?

  • Fernando

    Hey, nice tut, i have a question. How can add a radio and checkbox inputs, i try, but in the console i get this error : Notice: Undefined index:…please help, thanxs :) … ( sorry for my english )

  • A. Elyou

    hi thank you for sharing this, if any one got an error related to the $.param
    TypeError: Cannot read property ‘jquery’ of undefined

    you can replace this line
    if (contactform.$valid) {

    with this one
    if (contactform.$valid && $scope.formData!=undefined) {

  • MinerHQ

    I wanted to say that when Im submiting the mail, the page try to refresh but it dosent go back – Just white screen and a error ” Failed to load resource: the server responded with a status of 501 (Not Implemented) “

  • kanabi

    Hello, for several hrs I’ve been trying to find a way to submit an expression from a hidden input:

    The expression {{post.title}} comes from another controller within the form controller is nested. In the value field it is showing up correctly, but somehow it is not being passed on the ng-model and ng-init. I would appreciate any hints how to resolve this…

  • Eeshwar

    Hi, good tutorial, It working fine, but when try to access file attachment for mail, it saying “Could not access the file”, I tried to find name and path of the upload file in php class it getting c:fakepathabcd.txt., but it working file attachment also in html (angularjs framework). Can any one help ,” How to send file uploaded attachment mail sending using Angular JS and PHP Mailer

  • Joe Gren

    How do you make it so that the textarea field is not a mandatory field. Right now I have a large form and I don’t want all the fields to be required.

  • Eric Njanga

    Great job buddy. I’ve taken inspiration from your idea for one of my projects.

  • Slow

    Hi, I test the code it represent successful messgae but there is no email on my mailbox. Where is the completed form sent?

  • sameer

    how to add the “$to” email address to receive the data from the form which the user has submitted via email

  • sameer

    {“success”:false,”message”:”Message could not be sent. Mailer Error: Could not instantiate mail function.”}
    please help. why am i getting this error??

  • achari

    achari bhadra

  • Wanjila Abraham

    Hello Matt, you have been of much help, and i stumbled on your thread at a time that i greatly needed it, but after implementing it on my project i keep getting a 500 internal error.. I am stuck now would you be kind enough to offer some solutions?

  • Hello Journeyer

    Great bro

  • Corsaro

    i have one problem the form work fine when i wrok with the library anguraljs 1.2, but if i change the library with 1.4 or 1.5 after send the email open page HTTP Error 405.0 – Method Not Allowed

  • sanjay

    Message could not be sent. Mailer Error Could not instantiate mail function.