AngularJS Charts with CanvasJS

This example uses CanvasJS to chart Nintendo’s console sales in an AngularJS app. Clicking Bootstrap nav-tabs trigger the Angular controller to change the chart type (bar, column, pie chart etc).

nintendo-console-sales-chart

The file structure:
canvasjs-example-files

In index.html setup chartApp and add all the Angular directives, Bootstrap nav-tabs, and a div with id chartContainer for the CanvasJS chart:

<!DOCTYPE html>
<!DOCTYPE html>
<html ng-app="chartApp">
<head>
    <title>AngularJS Charts with CanvasJS</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="canvasjs.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
    <script src="directives.js"></script>
    <style>
        .container {
            margin-top: 40px;
            width: 970px !important;
        }
        #chartContainer {
            margin-top: 30px;
            height: 500px; 
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="container" ng-controller="ChartController">
        <div class="row">
            <div class="col-xs-12">
                <ul class="nav nav-tabs" mc-toggle-active>
                    <li class="active"><a href="" ng-click="changeChartType('bar')">Bar</a></li>
                    <li><a href="" ng-click="changeChartType('column')">Column</a></li>
                    <li><a href="" ng-click="changeChartType('line')">Line</a></li>
                    <li><a href="" ng-click="changeChartType('area')">Area</a></li>
                    <li><a href="" ng-click="changeChartType('pie')">Pie</a></li>
                    <li><a href="" ng-click="changeChartType('doughnut')">Doughnut</a></li>
                    <li><a href="" ng-click="changeChartType('scatter')">Scatter</a></li>
                </ul>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12">
                <div id="chartContainer"></div>
            </div>
        </div>
    </div>
</body>
</html>

In app.js assign the Angular app to a variable called app:

//chartApp does not have any dependencies
var app = angular.module('chartApp', []);

In controller.js setup the ChartController. This will create the chart and update it’s layout when the chart type changes:

//inject the $scope service into the controller
app.controller('ChartController', function($scope) {

    $scope.chart = new CanvasJS.Chart("chartContainer", {
        theme: 'theme1',
        title:{
            text: "Nintendo Console Sales"              
        },
        axisY: {
            title: "million units",
            labelFontSize: 16,
        },
        axisX: {
            labelFontSize: 16,
        },
        data: [              
            {
                type: "bar",
                dataPoints: [
                    { label: "Wii U", y: 6.17 },
                    { label: "Wii", y: 101.06 },
                    { label: "GameCube", y: 21.74 },
                    { label: "64", y: 32.93 },
                    { label: "SNES", y: 49.10 },
                    { label: "NES", y: 61.91 },
                    { label: "3DS", y: 43.33 },
                    { label: "DS", y: 153.99 },
                    { label: "Advance", y: 81.51 },
                    { label: "GameBoy", y: 118.69 },
                ]
            }
        ]
    });

    $scope.chart.render(); //render the chart for the first time
            
    $scope.changeChartType = function(chartType) {
        $scope.chart.options.data[0].type = chartType;
        $scope.chart.render(); //re-render the chart to display the new layout
    }

});

In directives.js setup a custom directive to handle changing the nav-bar tabs active class:

//when a tab is clicked make it active
app.directive('mcToggleActive', function() {
	return {
		link: function(scope, element, attrs) {
			element.find('li').on('click', function() {
                $(this).addClass('active').siblings().removeClass('active');
            });
		}
	}
});

View a demo here. Download the source files.

Links:
- Bootstrap Disable Responsive
- CanvasJS Documentation

Posted in Uncategorized | 1 Comment

Bootstrap 3 Responsive Breakpoint Values

The breakpoint values Bootstrap uses for media queries are pretty handy. The code below comes from the Bootstrap 3 Sass download in _variables.scss:

// Extra small screen / phone
// Note: Deprecated $screen-xs and $screen-phone as of v3.0.1
$screen-xs:                  480px !default;
$screen-xs-min:              $screen-xs !default;
$screen-phone:               $screen-xs-min !default;

// Small screen / tablet
// Note: Deprecated $screen-sm and $screen-tablet as of v3.0.1
$screen-sm:                  768px !default;
$screen-sm-min:              $screen-sm !default;
$screen-tablet:              $screen-sm-min !default;

// Medium screen / desktop
// Note: Deprecated $screen-md and $screen-desktop as of v3.0.1
$screen-md:                  992px !default;
$screen-md-min:              $screen-md !default;
$screen-desktop:             $screen-md-min !default;

// Large screen / wide desktop
// Note: Deprecated $screen-lg and $screen-lg-desktop as of v3.0.1
$screen-lg:                  1200px !default;
$screen-lg-min:              $screen-lg !default;
$screen-lg-desktop:          $screen-lg-min !default;

// So media queries don't overlap when required, provide a maximum
$screen-xs-max:              ($screen-sm-min - 1) !default;
$screen-sm-max:              ($screen-md-min - 1) !default;
$screen-md-max:              ($screen-lg-min - 1) !default;

Similarly the code below comes from the Bootstrap 3 Less download in _variables.less:

// Extra small screen / phone
// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1
@screen-xs:                  480px;
@screen-xs-min:              @screen-xs;
@screen-phone:               @screen-xs-min;

// Small screen / tablet
// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1
@screen-sm:                  768px;
@screen-sm-min:              @screen-sm;
@screen-tablet:              @screen-sm-min;

// Medium screen / desktop
// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1
@screen-md:                  992px;
@screen-md-min:              @screen-md;
@screen-desktop:             @screen-md-min;

// Large screen / wide desktop
// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1
@screen-lg:                  1200px;
@screen-lg-min:              @screen-lg;
@screen-lg-desktop:          @screen-lg-min;

// So media queries don't overlap when required, provide a maximum
@screen-xs-max:              (@screen-sm-min - 1);
@screen-sm-max:              (@screen-md-min - 1);
@screen-md-max:              (@screen-lg-min - 1);
Posted in Uncategorized | 1 Comment

AngularJS Twitter Authentication with OAuth.io

This rough example uses OAuth.io to connect an AngularJS app to Twitter’s OAuth API and display a user’s recent timeline. To get started create a new app on OAuth.io and add the Twitter provider (read this blog post for a good introduction).

The file structure:
Twitter OAuth.io example files

In index.html setup twitterApp and add all the Angular directives. Include OAuth.js in the header:

<!DOCTYPE html>
<html ng-app="twitterApp">
<head>
    <title>Twitter OAuth.io Example</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="oauth.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
    <script src="services.js"></script>
    <style>
        .container {
            margin-top: 10px;
            margin-bottom: 10px;
        }
        #results .row {
            margin-top: 15px;
            margin-bottom: 15px;

        }
    </style>
</head>
<body>
    <div class="container" ng-controller="TwitterController">
        <h1>Twitter OAuth.io Example</h1>
        <div class="row">
            <div class="col-xs-12">
                <button ng-click="connectButton()" id="connectButton" type="button" class="btn btn-primary">Connect Twitter</button>
                <button ng-click="refreshTimeline()" id="getTimelineButton" type="button" class="btn btn-info" style="display:none;">Get My Timeline</button>
                <button ng-click="signOut()" id="signOut" type="button" class="btn btn-link" style="display:none;">Sign Out</button>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12" id="results">
                <div class="row" ng-repeat="t in tweets">
                    <div class="col-xs-2 col-sm-1">
                        <img ng-src="{{t.user.profile_image_url}}" class="img-circle">
                    </div>
                    <div class="col-xs-10 col-sm-11">
                        <small>{{t.user.name}}</small><br>{{t.text}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

In app.js assign the Angular app to a variable called app and pass in twitterApp.services as a dependency:

//twitterApp is dependent on the twitterApp.services module
var app = angular.module('twitterApp', ['twitterApp.services']);

In services.js create a module called twitterApp.services and add a factory called twitterService. This factory handles communicating with OAuth.io. Inject Angular’s $q service into the factory to deal with asynchronous requests (watch this video to learn more about dealing with asynchronous issues):

angular.module('twitterApp.services', []).factory('twitterService', function($q) {

    var authorizationResult = false;

    return {
        initialize: function() {
            //initialize OAuth.io with public key of the application
            OAuth.initialize('e6u0TKccWPGCnAqheXQYg76Vf2M', {cache:true});
            //try to create an authorization result when the page loads, this means a returning user won't have to click the twitter button again
            authorizationResult = OAuth.create('twitter');
        },
        isReady: function() {
            return (authorizationResult);
        },
        connectTwitter: function() {
            var deferred = $q.defer();
            OAuth.popup('twitter', {cache:true}, function(error, result) { //cache means to execute the callback if the tokens are already present
                if (!error) {
                    authorizationResult = result;
                    deferred.resolve();
                } else {
                    //do something if there's an error
                }
            });
            return deferred.promise;
        },
        clearCache: function() {
            OAuth.clearCache('twitter');
            authorizationResult = false;
        },
        getLatestTweets: function () {
            //create a deferred object using Angular's $q service
            var deferred = $q.defer();
            var promise = authorizationResult.get('/1.1/statuses/home_timeline.json').done(function(data) { //https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
                //when the data is retrieved resolved the deferred object
                deferred.resolve(data)
            });
            //return the promise of the deferred object
            return deferred.promise;
        }
    }
    
});

In controller.js setup the TwitterController. This will call methods from twitterService and respond to user interactions. Inject twitterService into the controller along with $q and $scope:

//inject the twitterService into the controller
app.controller('TwitterController', function($scope, $q, twitterService) {

    $scope.tweets; //array of tweets
    
    twitterService.initialize();

    //using the OAuth authorization result get the latest 20 tweets from twitter for the user
    $scope.refreshTimeline = function() {
        twitterService.getLatestTweets().then(function(data) {
            $scope.tweets = data;
        });
    }

    //when the user clicks the connect twitter button, the popup authorization window opens
    $scope.connectButton = function() {
        twitterService.connectTwitter().then(function() {
            if (twitterService.isReady()) {
                //if the authorization is successful, hide the connect button and display the tweets
                $('#connectButton').fadeOut(function(){
                    $('#getTimelineButton, #signOut').fadeIn();
                    $scope.refreshTimeline();
                });
            }
        });
    }

    //sign out clears the OAuth cache, the user will have to reauthenticate when returning
    $scope.signOut = function() {
        twitterService.clearCache();
        $scope.tweets.length = 0;
        $('#getTimelineButton, #signOut').fadeOut(function(){
            $('#connectButton').fadeIn();
        });
    }

    //if the user is a returning user, hide the sign in button and display the tweets
    if (twitterService.isReady()) {
        $('#connectButton').hide();
        $('#getTimelineButton, #signOut').show();
        $scope.refreshTimeline();
    }

});

View a demo here. Download the source files.

Links:
- Twitter Apps Management
- Twitter GET statuses/home_timeline
- OAuth.io JavaScript API Reference

Posted in Uncategorized | 17 Comments

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.

angularjs-bootstrap-phpmailer-form

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

<!DOCTYPE html>
<html>
<head>
    <title>AngularJS Contact Form with Bootstrap and PHPMailer</title>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <link rel="stylesheet" href="style.css">
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
    <script src="app.js"></script>
    <script src="controllers.js"></script>
</head>
<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>
                <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>
                        </div>
                        <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>
                        </div>
                        <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>
                        </div>
                        <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>
                        </div>
                        <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
                                </button>
                            </div>
                        </div>
                    </form>
                    <p ng-class="result" style="padding: 15px; margin: 0;">{{ resultMessage }}</p>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

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.resultMessage;
    $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) {
            $http({
                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)
            }).success(function(data){
                console.log(data);
                if (data.success) { //success comes from the return json object
                    $scope.submitButtonDisabled = true;
                    $scope.resultMessage = data.message;
                    $scope.result='bg-success';
                } else {
                    $scope.submitButtonDisabled = false;
                    $scope.resultMessage = data.message;
                    $scope.result='bg-danger';
                }
            });
        } else {
            $scope.submitButtonDisabled = false;
            $scope.resultMessage = 'Failed :(  Please fill out all the fields.';
            $scope.result='bg-danger';
        }
    }
});

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:

<?php
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);
        exit;
    }

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

    $mail->From = $_POST['inputEmail'];
    $mail->FromName = $_POST['inputName'];
    $mail->AddAddress('something@test.com'); //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);
        exit;
    }

    $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.

Links:
- 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.

Posted in Uncategorized | 23 Comments