Monday, September 22, 2014

Web Interface Feedback with Long Running Server Processes, Using PHP and Angular's $interval Service

Recently I had to convert a CRON job that brings the data of 3 different databases together and generates approximately 2000 plus static pages to a manually started process via a web interface.  Here were my problems:

  1. How to let a long running process NOT time out under the contraints of the web server?
  2. How to periodically let the user know the process is still running without having the user think their browser is just hanging?
  3. How to do this inside a single page application using AJAX?

Tuesday, September 9, 2014

A Data Store For Your AngularJS Application

So I got some feedback from some other Angular users about my $globals service that I had written about in a previous post.  The service worked for me at the time and still does, but I've refined the approach to be more of a reusable module than a service you would need to rebuild for each application.  Gone are the hard coded configurations, the $ symbol (as this is reserved for core Angular services) and the name itself global.  I had originally used the global name to remind myself that this service was meant to house global application data for all modules and services in the application.  For this I got a lot of flack, as its not a true global situation as the service needs to be injected into modules that wish to use it.  In short the problem this was trying to solve was the extensive use of $rootScope as a communication and repository avenue.

I've now taken what was a rigid and somewhat clunky service and refined it.  I've changed the service over to a provider, to allow for application data storage and configuration during the application module's config function.  I find that creating services this way is more useful than creating a simple factory service.

First we need to decide what our data store needs to do:
  1. It needs to be able to store various pieces of information, whether they be strings, integers or even objects.
  2. We need to be able to retrieve that information easily.
  3. We may need to update pieces of already stored information.
  4. Be able to delete a specific piece of information.
This activity should look familiar to most of you, its your basic CRUD (Create, Read, Update & Delete) type storage.  We can accomplish this list with 3 service functions; set, get and delete.  These functions will operate on the service's internal "data" array.  So lets take a look at what we have so far.

angular.module('storage-services',[])
    .factory('appDataStoreSrvc',[function(){
        var _data = []; // our data storage array

        // service methods
        return {
            set : function(){

            }, // end set

            get : function(){

            }, // end get

            del : function(){

            }
       };
    }]); // end appDataStoreSrvc / storage-services

Monday, March 24, 2014

Using jQuery UI with AngularJS, A Pattern

I've seen this asked time and again, "How do I use jQuery UI's function X" with AngularJS, where 'X' is any jQuery UI interaction, effect or widget.  Well the answer is fairly simple; create a directive of course.

Before I begin you may want to take a look at the Angular-UI project on GitHub and see if the that covers any of the jQuery UI or even Bootstrap functions you're looking for before you go ahead and create your own.

I would also like to note that using jQuery UI with AngularJS requires you to use jQuery itself as well.  This may be a cause for concern for some as it can make the javascript for the page very heavy by some estimates, especially when you add in your application or page's code.  I personally don't mind so much for many of the projects I work on as they are more often then not internal and the concerns for page weight are minimal.  If you do go this route use a CDN, these libraries are so popular that its more than likely your user's browser will already have them cached from the same CDN you use and thus will not have to reload the library.

Lets take jQuery UI's drag and drop interactions also known as "draggable" and "droppable" and ready them for use with AngularJS.

First, lets decide that we just want to be able to add our directive as an attribute to an DOM element we already have on a page which would then enable that element to be dragged around. So here's how we want that to look:

<div id="myElement" class="myClass myOtherClass" angled-draggable>[...Some Content...]</div>

Friday, October 18, 2013

Redux: Creating an Application Dialog Service using AngularJS, Twitter Bootstrap & Angular UI-Bootstrap

Updates
In my previous post about creating modal/dialog service using Twitter Bootstrap and AngularJS, I used Angular UI Bootstrap version 0.5.0.  As of version 0.6.0 the modal directive has changed a bit, the dialog object (directive) is no longer available for injection into your controllers; instead a modal instance is now created. The directive is also under the module ui.bootstrap.modal instead of ui.bootstrap.dialog.

The changes are more than welcome in my opinion, have a look at the $modal service. One change in particular is the addition of a result promise on the modal instance. This alone reduced the amount of $scope.$on listeners and $rootScope.$broadcast statements I had to make in order to pass data back from modals or dialogs in my application to appropriate controllers. It also allowed me to eliminate all the $timeouts I had placed in my application due to waiting on modals/dialogs to fully close before possibly opening a new one. In the past, I found that the modal backdrop wasn't being destroyed in time before the next modal got created leaving an orphaned background in my application with no way of closing it - and thus rendering an application's interface unreachable. Now I can send the results of my modal/dialog back through the close or dismiss methods of the modal instance directly to the opening controller.

So I've rewritten my Angular dialog service to take advantage of the changes in Angular UI's Modal directive.  Hope this serves you well and let me know if find any problems.

Demo & Files

Demos:
  1. v1.0 Supports AngularJS 1.1.5 and below: Codepen - http://codepen.io/m-e-conroy/pen/ALsdF
  2. v2.0 Supports AngularJS 1.2+ with ngSanitize module: Codepen - http://codepen.io/m-e-conroy/pen/AmBpL
    Note: In order to use version 2.0 you'll need to include the Angular Sanitize module in your application.

What Do You Need?

Edit: It was pointed out to me by +Raf Kewl that if you're using AngularJS version 1.2 changes to the directive ng-bind-html-unsafe used in the dialog templates has been changed to just ng-bind-html and you'll need to include ngSanitize for the $sanitize object and for ng-bind-html to work.
  1. AngularJS - www.angularjs.org (version 1.1.2+, although I believe it will work with versions 1.0.4 (version 1.1.5 and less)
  2. Angular UI Boostrap - http://angular-ui.github.io/bootstrap/#/getting_started (version 0.6.0+, I highly recommend creating a custom version, you don't want to be sticking unused directives in your application)
  3. Twitter Bootstrap CSS - http://getbootstrap.com/ (version 2+, I've used version 3 successfully with this)
  4. Dialogs.js - https://github.com/m-e-conroy/angular-dialog-service (or dialogs.min.js)

Monday, September 16, 2013

Don't pollute $rootScope, create a $global service instead.

Don't dirty up the $rootScope by setting a bunch of global variables, functions and listeners.  If at all possible I try my hardest to avoid injecting and using $rootScope in my controllers, directives or services.  I have, in my projects, had the need to have global variables and functions though.  So how have I gotten around my self imposed rule?  Simple.  Create a "global" service with application variables that all controllers might possibly need and then inject the service when needed.  Its a great way to keep things all in one place and saves time later when looking through a boat load of controllers and services for a particular variable's default or initial value.

One of the first things I do when starting a project is to create this service for my application.
angular.module('myapp.services',[])

    .factory('$global',['$http',function($http){
        return {};
    }]); // end $global
I usually inject the $http object into this service to create a global request function to use in other services, you'll see this in the next code excerpt. Its just a time saver, to keep from writing the same code over and over again, also if later you wanted to change your application to use maybe $resource you would only have to configure it or change it in one place. Plus you won't have to inject $http into your other services when mostly likely you are already inject the $global service object we're creating.
angular.module('myapp.services',[])

    .factory('$global',['$http',function($http){

        var _urls = {
            ldap : 'ldap/find/',
            subjects : 'https://api.somedomain.com/catalog/subjects';
            course : 'https://api.somedomain.com/catalog/courses/';
        }; // end urls

        return {
            request : function(url,vars){
                if(angular.isDefined(vars)){
                    return $http.post(url,$.param(vars),{headers:{'Content-Type': 'application/x-www-form-urlencoded'}});
                }else{
                    return $http.get(url);
                }
            },

            url : function(which){
                return _urls(which);
            } // end url
        };
    }]) // end $global
Here I've added two methods, request and url, plus a private variables _urls. The request method just returns the promise from the $http object and performs a POST or GET based on whether you're passing variables to the endpoint URL or not. The url method returns a string with the URL request from our list of URLs. I've put in some fake URLs to illustrate. Now in a controller, service or wherever you may inject your $global service you could make a call like this:
var subj_url = $global.url('subjects');
I know it doesn't seem like much but you'll see in a moment the advantages of the $global service.

I work at a University so I deal with writing applications against databases with subjects, courses, scheduling and students all the time.  For an example, let us say we want to create a single page application and in that application there is an HTML select element with a list of subjects to choose from; but before we create that dropdown list we first need to retrieve the list itself.  Lets create a service to interact with our database backend and retrieve that list.  We don't want to add this functionality to $global because we just want $global to manage global variables and a few select functions. So what does our subjects service look like?
    .factory('subjectSrv',['$global,function($global){
        //-- Variables --//
        var _send = $global.request;

        //-- Methods --//
        return {
            subjects : function(){
                return _send($global.url('subjects'));
            }, // end subjects

            course : function(abbr,num){
                var url = $global.url('course');
                if(angular.isDefined(abbr) && !(angular.equals(abbr,null) || angular.equals(abbr,'')))
                    url += 'abbr/' + abbr + '/';

                if(angular.isDefined(num) && !(angular.equals(num,null) || angular.equals(num,'')))
                    url += 'num/' + num;

                return _send(url);
            } // end course
        };
    }]); // end subjectSrv / module(myapp.services)
Ok, so now we have our service. What is it doing? You'll notice that our $global service is injected into this service, this is because we want to share the request method I set up with our new service. You can see that the private variable _send is now set to $global's request method; now all we need is to call _send when we want to make a request at one of our API endpoints. You'll also notice that I make calls to $global's url method to retrieve the URL for the API endpoint I want. This comes in handy when your service starts to grow or the number of services you have in a large application grows, you will know exactly where your URLs or any variable you may use across services are. This also makes it easy for testing later, you could mock your $global object, or just set up $global to use Angular's $httpBackend instead.

Our subjects endpoint returns a JSON object with all the subjects and a some information about each subject.  It looks similar to this:
[
    {"abbr":"AAP","subject":"Arts Management Program","academic_org":"0177","cip_code":"50.0704","hegis_code":null,"effective_date":"1901-01-01","courses":"29"},
    {"abbr":"AAS","subject":"African-American Studies","academic_org":"0734","cip_code":"05.0201","hegis_code":"22.11","effective_date":"2009-05-18","courses":"97"},
    {"abbr":"AFS","subject":"African Studies - Intl Studies","academic_org":"0356","cip_code":"05.0101","hegis_code":"03.05","effective_date":"1901-01-01","courses":"3"},
    {"abbr":"AHI","subject":"Art History","academic_org":"1379","cip_code":"50.0703","hegis_code":"10.03","effective_date":"1901-01-01","courses":"136"},

    ...

    {"abbr":"YID","subject":"Yiddish","academic_org":"0162","cip_code":"16.9999","hegis_code":"11.11","effective_date":"1901-01-01","courses":"2"},
    {"abbr":"YOR","subject":"Yoruba","academic_org":"0356","cip_code":"16.9999","hegis_code":"11.16","effective_date":"1901-01-01","courses":null}
]
You can see that its an array of JSON encoded objects with some properties about each subject. So lets create our dropdown, I'm just going to show the HTML select element, I'm not going to build out a whole application interface. Actual in my interfaces where I have used this I've used the Angular UI Bootstrap Typeahead directive instead of the HTML select element and it works great.
<div ng-controller="subjectCtrl">
    <select ng-model="selectedSubject">
        <option ng-repeat="subj in subjects" value="{{subj.abbr}}">{{subj.abbr}}: {{subj.subject}}>/option<
    </select>
</div>
That's it, that is our whole select element. Now onto our controller:
angular.module('myapp.controllers',['myapp.services'])

    .controller('subjectCtrl',['$scope','$global',function($scope,$global){
        $scope.subjects = $global.getSubjects();
        $scope.selectedSubject = null;
    }]); // end subjectCtrl / module(myapp.controllers)
Pretty simple huh? But you're probably wondering where the method $global.getSubjects() is. Ok so we need to add a few more methods to our $global service.
angular.module('myapp.services',[])

    .factory('$global',['$http',function($http){
        //-- Variables --//
        var _urls = {
            ldap : 'ldap/find/',
            subjects : 'https://api.somedomain.com/catalog/subjects';
            course : 'https://api.somedomain.com/catalog/courses/';
        }; // end urls

        var _subjects = [];

        //-- Methods --//
        return {
            request : function(url,vars){
                if(angular.isDefined(vars)){
                    return $http.post(url,$.param(vars),{headers:{'Content-Type': 'application/x-www-form-urlencoded'}});
                }else{
                    return $http.get(url);
                }
            },

            url : function(which){
                return _urls(which);
            }, // end url

            setSubjects : function(data){
                _subjects = data;
            }, // end setSubjects

            getSubjects : functions(){
                return _subjects;
            } // end getSubjects
        };
    }]) // end $global
Here we added a private variable _subjects to our $global service and then created getter and setter methods for that variable. So now what? Where do we load this? Subjects don't change much here at the University so I'd like to set it in our application once and not have to make a call to our API endpoint every time the ngView changes, hence the reason we're saving this in the $global services. I would also like the information to be available right away when my application loads, so the perfect place to do this would be in our application's run method.
angular.module('myapp',['myapp.services','myapp.controllers'])

    .config([...])

    .run(['$global','subjectSrv',function($global,subjectSrv){
        $global.setSubjects(subjectSrv.subjects().then(function(response){
            return response.data;
        }));
    }]); // end run / module(myapp)
Done! When our application loads it will resolve what is in the run method, where we make a call to our API endpoint for subjects with our subject service which them returns the data to our $global service's setSubjects method, when our application is ready to go our $global service will have all the subjects necessary to feed any controller or other service.

Monday, August 12, 2013

Creating an Application Dialog Service using AngularJS, Twitter Bootstrap & Angular UI-Bootstrap

Attention!! - The ui.bootstrap.dialog is no longer available in version 0.6.0 of Angular UI Bootstrap, for the the modules described below to work you'll need version 0.5.0 or below. In version 0.6.0 $dialog has been replaced with $modal, I'll be posting a "Part II" on this soon.

Dialogs and modals are a big part of any application whether native or web-based. They serve to inform the user of actions performed, in progress or of items that need attention. Here I'm going to show you how to create a reusable dialog service in +AngularJS  to use in your web-based +JavaScript  applications.

Requirements

  1. Twitter Bootstrap v.2 (I haven't tested this with v.3)
  2. AngularJS v.1.0.4+ (I initially used v.1.0.4 in creating this service but have used it with 1.1.5 as well)
  3. Angular UI Bootstrap (I suggest creating a customized version with only the directives you'll need, just make sure you include the dialog directive when creating your customized version)

Optional Items

  • jQuery (AngularJS includes a subset of jQuery so this isn't entirely necessary)
  • Perhaps a Twitter theme?  I suggest +Marcello Palmitessa's Bootplus theme, its very clean and well done especially if you like Google+.  If not then there are a good selection of free themes at Bootswatch, I especially enjoy using the darker Cyborg theme for applications.

Thursday, April 18, 2013

jQuery FX using AngularJS

I've seen many posts with developers frustrated with +AngularJS trying to use +jQuery FX like fade or slide.  Many responses just say they should create a directive to solve the problem, well here's how that directive might look.