Category Archives: AngularJS

<select element problem in AngularJS

So I just spent 10 minutes trying to figure out why the following wasn’t working in AngularJS:

<select ng-options="item.name for item in items">
</select>

I spent ages checking my syntax and that the data was correct. Finally I discovered the reason – <select REQUIRES ng-model.

<select ng-model="dummy" ng-options="item.name for item in items">
</select>

works just fine…

A Facebook Share component for AngularJS

There are several facebook share components available for AngularJS however I was needing something that could:

  • Show a count of the number of shares
  • Work within the translation infrastructure of my app (ie a custom template)
  • Handle different URL mappings for pages – whilst a typical angular url might be http://app.com/#/path/… facebook can’t scrape this unless you use the #! method or somesuch. Because of this typically on the server we map to different links such as http://app.com/landing/path/…

The below code snippets work together to do all of this in a few lines:

angularApp.directive("ngFbShare", function($http, $rootScope) {
    return {
        restrict: 'A',
        templateUrl: 'views/components/fbshare.html',
        scope: {
            ngFbShare: "@",
        },
        link: function(scope, elem, attrs) {
            var link; // always the latest link relative to the base of the server. Use this for click tracking etc
            var update_share_link = function() {
                link = '/landing/' + scope.ngFbShare;
                scope.share_link = MY_FULL_ADDRESS + link;
            };
            update_share_link();
            scope.$watch('ngFbShare', update_share_link);

            scope.fb_total_count = 0;
            $http.get('http://api.facebook.com/method/links.getStats?format=json&urls=' + scope.share_link)
                .then(function(res) {
                    scope.fb_total_count = res.data[0].total_count;
                });                
        }       
    };      
});             

<a href="http://www.facebook.com/sharer/sharer.php?u={{ share_link }}" target="fb_share_window" class="btn btn-primary btn-fb">
    <i class="fa fa-fw fa-facebook"></i> Share
    <span class="count">{{ fb_total_count }}</span>
</a>

Background Slideshow with AngularJS and Bootstrap

As part of a project we wanted to have the front page with a nice rotating background for the jumbotron. There are a number of carousel components and scripts that can be easily found online but mostly they use the img tag and/or require a root absolute div which means it won’t automatically resize to the jumbotron content. I wanted a jumbotron that would resize to the content and also provide a nice seamless transition for the images. So, I sat down and rolled my own.

Firstly you need to set up a jumbotron component:

.jumbotron-slideshow {
    position: relative;
    background-color: transparent;  // replace the standard bootstrap background color

    .slideshow {
        background-size: cover;
        background-repeat: no-repeat;
        background-position: 50% 50%;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        
        /* Layer the images so that the visible one is below all the others,
         * but the previously active one fades out to reveal the visible one
         * below */
        transition: opacity 1s;
        opacity: 0;
        
        &.visible {
            transition: none;
            opacity: 1;
            z-index: -1;
        }
    }   
}       

And then the HTML:

<div class="jumbotron jumbotron-slideshow">
    <div ng-bg-slideshow="[ 'images/bg1.jpg', 'images/bg2.jpg', ... ]" interval=5000></div>

    ... content that you want ...

Create the angular template to generate the image divs:

<div ng-repeat="img in images"
        class="slideshow" ng-class="{ visible: active_image == $index }" ng-style="{ 'background-image': 'url(' + img + ')' }">
    </div>  

And finally the Angular component:

app.directive("ngBgSlideshow", function($interval) {
    return {
        restrict: 'A',
        scope: {
            ngBgSlideshow: '&',
            interval: '=',
        },
        templateUrl: 'views/components/slideshow.html',
        link: function( scope, elem, attrs ) {
            scope.$watch( 'ngBgSlideshow', function(val) {
                scope.images = val();
                scope.active_image = 0;
            });

            var change = $interval(function() {
                scope.active_image++;
                if( scope.active_image >= scope.images.length )
                    scope.active_image = 0;
            }, scope.interval || 1000 );
        
            scope.$on('$destroy', function() {
                $interval.cancel( change );
            });
        }
    };  
});         

Note: If you want to be able to programatically change the interval you’ll need to add a watch that recreates the interval when the interval attribute changes.

Easy ticks and crosses using FontAwesome

I spent a few minutes knocking up a comparison table for a project today and wanted an easy way to have ticks and crosses. After a bit of experimenting I found out that the excellent FontAwesome project makes this quite easy:

<span class="fa-stack">
    <i class="fa fa-circle fa-stack-2x"></i>
    <i class="fa fa-times fa-stack-1x fa-inverse"></i>
</span>
<span class="fa-stack">
    <i class="fa fa-circle fa-stack-2x"></i>
    <i class="fa fa-check fa-stack-1x fa-inverse"></i>
</span>

Per-component loading spinner for AngularJS

One of the first things that people want to do with AngularJS is to have a loading spinner on their page to prevent the unseemly appearance of a page with no content loaded because you’re waiting on an ajax xhr request. There are quite a lot of these spinner plugins available, or you can relatively easily roll your own.

However most of these are whole-page ie if any infly request is happening, the whole page appears blocked to the user. This can be quite annoying and give the impression of your site being pretty slow. What other sites heavily dependent on ajax (eg Facebook and LinkedIn) typically do is have each individual block/component on the page display a loading graphic so that perhaps your friends list is marked as loading but your news feed had already loaded.

Fortunately with AngularJS’s awesome scope, factory and component design it’s very easy to bolt this on to an existing app in just a few minutes. Let’s look at some code.

Firstly, (as you should be doing already) you need to have your ajax request going through a single point in your code such as the skeletal factory below. I’d typically do something like this:

angularApp.factory('api', function( $http ) {
    var fns = {};
    var req = function( path, args, opts ) {
        var promise = $http.post( fns.get_url(path), args );

        return promise.then(function(res) {
            return res.data;
        });
    };

    // Two calls - nonblocked which doesnt show the spinner and req which does
    fns.nonblocked = req;
    fns.req = req
    return fns;
});

Then we extend this so that the req function can have a scope passed in which will have a variable called infly_http_request which contains the number of outstanding ajax requests under that scope. We now add this in to the api service replacing the req function with something that will check the requests:

    ...
    function setup_spinner( scope ) { 
        if( scope.hasOwnProperty('infly_http_request') )
            return;

        scope.infly_http_request = 0;
        
        var cur_timeout;
        scope.stop_blocked_request = function( ) { 
            if( cur_timeout )
                $timeout.cancel(cur_timeout);
                
            scope.infly_http_request--;
     
            if( scope.infly_http_request < 0 ) 
                scope.infly_http_request = 0;
        };  
        scope.start_blocked_request = function( ) {
            if( cur_timeout )
                $timeout.cancel(cur_timeout);

            cur_timeout = $timeout(function() {
                scope.stop_blocked_request( );
                // XXX raise error
            }, 10000);

            scope.infly_http_request++;
        };
    }
    fns.req = function( path, args, opts ) {
        if( !opts )
            opts = {};

        var scope = opts.scope || $rootScope;
        setup_spinner( scope );

        scope.start_blocked_request();
        return req( path, args, opts )
            ['finally'](function() {
                scope.stop_blocked_request( );
            });
     };

Basically if a scope option is passed in this will scope the spinner to that block, otherwise it will use the global scope so you can still do a whole-page lock.

Finally here’s a quick directive to apply to a nice and easy spinner using fontawesome:

// XXX has to be a subdirective to an ngController - can't be on the same level as it.
window.angularApp.directive('showSpinner', function() {
    return {
        transclude: true,
        template: '<div><ng-transclude ng-show="infly_http_request == 0"></ng-transclude><div ng-hide="infly_http_request == 0" class="subspinner-container"><i class="fa fa-cog fa-spin"></i></div></div>',
    }
});

And the LESS (CSS) to go with it:

@subspinner-size: 3em;
.subspinner-container {
    text-align: center;
    .fa-spin {
        font-size: @subspinner-size;
    }
}

You can then write your Angular component and HTML as:

angularApp.controller('Product.List', function( $scope, api ) {
    api.req( '/api/path', { data... }, { scope: $scope } )
        .then(...)
});

<div ng-controller="Product.List">
  <div show-spinner>
    ...
  </div>
</div>

Anything within the show-spinner container under the controller and the scope attribute passed in the req() call will be replaced by a spinner while the request is in progress. If not you can have something in the main body of your page to show a spinner like:

<div ng-if="infly_http_request" class="spinner-container">
    <div id="spinner">
        <i class="fa fa-cog fa-spin"></i>
    </div>
</div>

@spinner-size: 5em;
.spinner-container {
    position: fixed;
    top:0;
    left:0;
    right:0;
    bottom:0;
    z-index:10000;
    background-color:gray;
    background-color:rgba(70,70,70,0.2);
    #spinner {
        position: absolute;
        font-size: @spinner-size;
    
        margin-left: -0.5em;
        margin-top: -0.5em;

        z-index: 20000;
        left: 50%;
        top: 50%;
    }
}

Stop Grunt minifying libraries all the time

Recently I’ve been playing around with using a proper build system for my latest Angular project. I chose to start with grunt which seems very powerful if quite difficult to set up (mostly because yeoman did most of the initial config with it). However I find it very strange that by default the grunt-usemin plugin tries to minify and then concat all of the libraries even those such as jquery or angular. This is both not very efficient (as there are already .min.js files distributed with them), also it probably can’t do it as well as they can. So, I started doing a bit of research as to how this could be avoided and came up with the following.

Firstly install the grunt-usemin-uglifynew module:

npm install --save-dev grunt-usemin-uglifynew

Then, change your Gruntfile to look like this:

  var uglifyNew = require('grunt-usemin-uglifynew');
  grunt.initConfig({
    ...
    useminPrepare: {
    ...
      options: {
        flow: {
          html: {
            steps: {
              js_min: [uglifyNew, 'concat'],
              js: ['concat', 'uglifyjs'],
    ...
    usemin: {
      ...
      options: {
        blockReplacements: {
            // copy of js block replacement fn from fileprocessor.js
            js_min: function (block) {
              var defer = block.defer ? 'defer ' : '';
              var async = block.async ? 'async ' : '';
              return '<script ' + defer + async + 'src="' + block.dest + '"><\/script>';
            }
        },

and your html file(s) to look like:

    <!-- build:js_min(.) scripts/vendor.js -->
    <!-- bower:js -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script src="bower_components/angular-route/angular-route.js"></script>
    <!-- endbower -->
    <!-- endbuild -->

        <!-- build:js({.tmp,app}) scripts/scripts.js -->
        <script src="scripts/app.js"></script>
        <script src="scripts/controllers/main.js"></script>
        <script src="scripts/controllers/about.js"></script>
        <!-- endbuild -->

Basically this splits the js processor into two – js (the normal one) remains unchanged and continues to concat all the files in the block and then minify them. The other one, js_min just tries to find the .min.js file and then concats into a single file (I wish there was an easy way to avoid having to concat them – I think it should really just copy the .min.js to the build directory and update the links to point to them there)

Better Database Search Functionality in 4 Simple Steps

As Google and other search engines are so good at predictive search these days users (ie me) get very frustrated at poor search results on websites or input boxes. Something I try to use across my apps is a decent auto-complete search interface however so many websites are very poor at this either matching only the first part of the string or matching any part of the substring. Additionally sometimes they don’t handle differences in case properly, certainly they don’t usually work with different accenting marks or order particularly well (eg search for beyoglu doesn’t usually return results like Beyoğlu). So, here follows a simple suggestion and code design pattern about how to implement this properly in PostgreSQL (Also works in MySQL although the regex matching code is slightly different). You can then have great instant typeahead functionality for example using the excellent AngularJS Bootstrap Typeahead input. I’ve implemented this in Perl/DBIC but it is a pattern that can be easily applied to any language such as Ruby/Rails or NodeJS.

Whilst there are a number of different search options out there that can plug into existing databases such as ElasticSearch, Sphinx or MySQL/Postgres fulltext search these are often fiddly to set up and are more intended for natural fulltext than for simple phrases or keywords which is what I generally aim for. The below method is pretty quick and easy to set up and allows you full control over the rebuilds, stemming, common word removal etc which is especially important for multi-lingual sites. You can also easily switch between database servers without having to totally redo your search functionality using this method.

Step 1: Add Column to Database Tables

Firstly, for any table you wish to search create a searchdata column probably varchar, with the maximum length of the data you’ll want to be searching (eg article title, author etc combined). For example:

alter table article add searchdata varchar(255) not null default '';

Step 2: Create Search Query Normalization Code

Then in your code create two routines to normalize any search text. Here is a (perl) example from my code:

package OradanOraya::Search;
use strict;
use warnings;
use utf8;
    
use Text::Unidecode 'unidecode';

sub to_search_no_strip {
    my ($self, $txt) = @_;
    $txt = lc unidecode($txt);
    $txt =~ s/[^a-zA-Z0-9 ]/ /g;  # kill newlines or space runs
    $txt =~ s/\s+/ /g;  # kill newlines or space runs
    $txt =~ s/^\s+|\s+$//;
    return $txt;
}
    
sub to_search {
    my ($self, $txt) = @_;
    $txt = $self->to_search_no_strip($txt);

    # common words to strip out
    $txt =~ s/\b(?: bolum | hastane | doktor | doctor | doc | dr )\S*//xg;

    return $txt;
}       
            
1

The first function is purely for normalizing the search terms (firstly stripping accents using the excellent Text::Unidecode module, then killing any non-alphanumeric chars, ensuring only one space between words and no spaces beginning or end of the text), the latter function does the same but also removes any common words you don’t want indexed.

Step 3: Set Columns to Auto Update in Your ORM

In your ORM base-class (you are using an Object-Relational Mapper rather than plain SQL right?) create some functions to handle the auto-population of these fields when the rows get updated by your code. For Perl’s DBIx::Class users here’s the code you inject into your DBIC Result base class. The first function, _get_searchdata is the key one that takes a specified list of columns, normalizes them and returns the searchdata field. The other functions are for the manual refresh of the search data in the row, automatically updating search data on update and create respectively:

sub _get_searchdata {
    my ($self) = @_;

    return My::Search->to_search( join ' ', map { $self->$_ || '' } $self->searchdata_columns )
}

sub refresh_searchdata {
    my ($self) = @_;
    $self->update({
        searchdata => $self->_get_searchdata
    });
}

sub set_column {
    my $self = shift;

    my $ret = $self->next::method( @_ );

    if( $self->can('searchdata') ) {
        # Note that we call the super-class set_column update method rather than ourselves otherwise we'd have an infinite loop
        $self->next::method( 'searchdata', $self->_get_searchdata );
    }

    return $ret;
}

sub insert {
    my $self = shift;

    if( $self->can('searchdata') ) {
        $self->searchdata( $self->_get_searchdata );
    }

    return $self->next::method( @_ );
}

In any of your tables where you have added a searchdata column create a method that just returns what columns you want to add to searchdata:
sub searchdata_columns { qw< title name > }

Step 4: Search Queries and Ordering

Whenever a row is added or updated now you’ll have the normalized search text added (see below for a script to auto-populate if you have existing data). To do nice searches you can now execute the following SQL (for MySQL replace ~ with REGEXP operator):

select * from article where searchdata ~ 'foo'

This will match the text anywhere. If you want to only match words beginning with this you can use PostgreSQL’s zero-width start-of-word \m operator (in normal regexp language this would be roughly equivalent to \b although that matches beginning and end of words):
select * from article where searchdata ~ '\mfoo'

If you want to order results whereby those with beginning-of-string matches go first, then the rest are alphabetical you can do something like (note the !~ as false orders before true):
SELECT *
FROM article
WHERE searchdata ~ '\mfoo'
ORDER BY searchdata !~ '^foo', searchdata

Well that’s a job well done! You can look at using some sort of index in the database to speed this up but to be honest for tables with less than 10k rows that’s probably not worth while. You’ll need to look at the trie type indexes that Postgres has, I don’t believe MySQL is able to index these sorts of searches.

The DBIC code for this last one:

my $search_str = quotemeta($fn->to_search( $p->{search} ));
$dbic->resultset('Article')->search({
  searchdata => { '~' => '\m' . $search_str }
}, {
  order_by => [
         \[ 'searchdata !~ ?', [ str => '^' . $search_str ] ],
         'searchdata'
  ]
});

Extra Step: Create a Reindex Script

You’ll also want to write some code to find any tables with searchdata and update them for initial population. Here’s the perl/dbic solution for this again but should be simple enough with any ORM (note the transaction commit per 100 updates which significantly improves performance on all database servers):

$|++;
my $count = 1;
$dbic->txn_begin;
for my $table ($dbic->sources) {
    my $rs = $dbic->resultset($table);

    next unless $rs->result_source->has_column('searchdata');

    while( my $d = $rs->next ) {
        if( $count++ % 100 == 0 ) {
            $dbic->txn_commit;
            $dbic->txn_begin;
            print ".";
        }
        
        $d->refresh_searchdata;
    }           
}           
$dbic->txn_commit;

Having both a require and a controller in a directive

I’ve been playing around with AngularJS directives a lot recently trying to modularize out some commonly used bits of code – particularly form validation stuff. As part of this I often want a controller in the directive (so I have child scopes reach in and add elements or trigger events), but I also want to be able to access some parent angular things such as the ‘form’ directive’s controller.

Typically if you have a controller in a directive it gets passed in as the 4th argument to the link function eg:

angularApp.directive('ngValidSubmit', function() {
    return {
        controller: function($scope, $element, $attrs) {
        },
        link: function(scope, elem, attr, ngValidSubmitCtrl) {
        },
    }
});

However if you have a require argument it also gets passed in as the 4th element in link which unfortunately trumps our controller:

angularApp.directive('ngValidSubmit', function() {
    return {
        require: 'form',
        controller: function($scope, $element, $attrs) {
        },
        link: function(scope, elem, attr, formCtrl) {
        },
    }
});

I couldn’t find anything else referencing this problem on the internet, so as a hunch I tried (what turned out to be correct) multiple require arguments including requiring the directive itself:

angularApp.directive('ngValidSubmit', function() {
    return {
        require: [ 'form', 'ngValidSubmit' ],
        controller: function($scope, $element, $attrs) {
        },
        link: function(scope, elem, attr, ctrls) {
            var formCtrl = ctrls[0];
            var ngValidSubmitCtrl = ctrls[1];
        },
    }
});

Hopefully this helps someone!

Formatting time inputs nicely with AngularJS

Unfortunately the AngularJS code uses a fixed formatter for the time when a browser doesn’t support the time input natively (eg firefox). The code specifies ‘HH:mm:ss.sss’ which is pretty pointless for most users I would imagine (I’m building a calendering app). Fortunately we can hook into this formatter and fix it, although the ideal would be to override it unfortunately there are local variables that we can’t override particularly easily. So I took the easy route of just modifying the string after it had already been parsed:

/* Attach to input time elements and switch their formatting to be HH:MM
 */             
angularApp.directive('ngModel', function( $filter ) {
    return {
        require: '?ngModel',
        link: function(scope, elem, attr, ngModel) {
            if( !ngModel )
                return;
            if( attr.type !== 'time' )
                return;
                    
            ngModel.$formatters.unshift(function(value) {
                return value.replace(/:00\.000$/, '')
            });
        }
    }   
});         

Extending Array functionality safely in Javascript

So I’m using AngularJS to create some tables of data and want to have a simple way to sum the columns at the bottom. I could write a function and attach to $scope however I’d like to be able to use this with a minimum of typing and have it the same in the templates as in Javascript. A good situation to extend Array.prototype.

Array.prototype.sum_col = function(col) {
        var t = 0; 
        for( var i = 0; i < this.length; i++ )
            t += parseFloat( this[i][col] );
            
        return t;
    };

Nice and easy. However then I noticed that certain pages of my Angular app didn't load with a very random error. After some debugging in the chrome JS browser it came down to the fact that whilst the above 3-statement for loop works fine, if I now do for( var i in array ) it also includes the sum_col function. D'oh. So after some research on stackoverflow I figured out the correct way to add stuff to Array.prototype which should probably be done by default whenever you want to:

Object.defineProperty( Array.prototype, "sum_col", {
    enumerable: false,
    value: function(col) {
        var t = 0; 
        for( var i = 0; i < this.length; i++ )
            t += parseFloat( this[i][col] );
            
        return t;
    }       
});