• DataTables TableTools Extension with AngularJS/RequireJS

    We use a flexible jQuery plug-in called DataTables at my job to handle generating large display tables. One often-requested feature was the ability to download the table as a CSV file. DataTables has an extension called TableTools that I used to implement this feature. TableTools lets you copy the table to the clipboard, export to CSV, XLS, and PDF, and also gives a clean, large view suitable for printing. Getting TableTools to work w/ AngularJS was a bit challenging.

    // Define as an AMD module if possible
    if ( typeof define === 'function' && define.amd ) {
    	define( ['jquery', 'datatables'], factory );
    }
    else if ( typeof exports === 'object' ) {
        // Node/CommonJS
        factory( require('jquery'), require('datatables') );
    }
    else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
        // Otherwise simply initialise as normal, stopping multiple evaluation
        factory( jQuery, jQuery.fn.dataTable );
    }

    ^ This logic at the very end of dataTables.tableTools.js  was conflicting with RequireJS. Specifically, require.js was properly loading the DataTables file (jquery.dataTables.js), but the conditional block here was trying to load dataTables.js. A 404 error was being thrown. I only needed the very last branch of the if statement:

    factory( jQuery, jQuery.fn.dataTable);

    You can check if TableTools (or another extension) is being properly loaded by using the DataTables Debug Bookmarklet. The debug bookmarklet shows you what extensions are correctly installed: Screen Shot 2014-07-16 at 6.50.46 PMdownload it from here. Once TableTools is installed, you should be able to use it with code like this:

          # Set up the TableTools DataTables extension for CSV exporting
          _createTableTools = (myDataTable) ->
            tt = new $.fn.DataTable.TableTools( myDataTable,
              sSwfPath: "/path/to/TableTools/swf/copy_csv_xls.swf"
              aButtons: [ "csv" ]
            )
    
            $(tt.fnContainer()).insertBefore('div.dataTables_wrapper');

    That’ll place a CSV download button right above an existing DataTable. I call this function after I create a table, passing in the DataTable instance as a parameter. Note that /path/to/TableTools/swf/copy_csv_xls.swf is from the site root, not the machine’s filesystem root. Note also that div.dataTables_wrapper is created by default when the DataTable is made–you don’t have to create that element yourself. You can also create the TableTools object as a property of the DataTable. Something like this should work:

            table = elem.find('table').dataTable(
              sDom: 'T<"clear">lfrtip'
              tableTools:
                sSwfPath: '/path/to/TableTools/swf/copy_csv_xls.swf'
              # Other table properties
              # ...
            )

    Repositioning the buttons anywhere besides on the DataTable is…well…annoying. I think you have to first initialize the TableTools DOM element on the DataTable, and then remove it from its parent node and append it to another node. Even if you get the DOM element in the correct place, I found there were a few CSS gotchas. I won’t write about them here, as the changes a coworker and I made were specific to our layout. I had to change some of the default styles created within the ZeroClipboard_TableTools.Client.prototype section of dataTables.tableTools.js. If the buttons appear but aren’t working, ensure that the path to the SWF file is correct and that the actual SWF element in the DOM is in the correct space. When I moved the buttons from their default position, I found the SWF element was being absolutely positioned, which in this particular layout made it sit invisibly in the top left-hand corner of the browser window. When you’re done though, you’ll have export functionality sitting right alongside the DataTable. Screen Shot 2014-07-16 at 7.12.22 PM


  • Weird ellipsis in AngularJS&#8230;

    Today at work I came across a strange issue.

    I had written a function that would take some raw numeric data, then format it to be displayed. For instance, ‘1.234567’ might become ‘$1.23’.

    In Angular I would normally just do plain old data binding like this:

    $scope.adjustedValue = '$1.23'
    <div></div>

     

    That was working fine for most cases, but some of our values required special formatting:

    $scope.adjustedValue = '<span class="prefix">$</span>1.23'

     

    When I used ng-bind-html to let me bind a variable containing HTML, however, an ellipsis was being appended to the end of my number.

    <div ng-bind-html="adjustedValue"></div>

     

    Instead of ‘$1.23’ as I expected, I was seeing ‘$1.23…’.

    I thought there was maybe some CSS being applied that was inserting that ellipsis, but when I disabled CSS the rogue ‘…’ still appeared.

    Binding with double curly braces behaved as expected, but using ng-bind-html did not. Weird.

    According to the docs for ngBindHtml: “By default, the innerHTML-ed content will be sanitized using the $sanitize service.”

    I tried bypassing the $sanitize step to see if that would solve this ellipsis problem (I also had to inject $sce into my controller):

    $scope.adjustedValue = '<span class="prefix">$</span>1.23'
    
    $scope.bypassSanitize = ->
        $sce.trustAsHtml($scope.adjustedValue)
    <div ng-bind-html="bypassSanitize()"></div>

     

    That resolved the problem; the output no longer contained an ellipsis. Always kind of unsettling when you’re not exactly sure why something works…

    The $sanitize method will remove any unclean HTML from your bound variable, so forgo it with that caveat. Indeed, when I set $scope.adjustedValue = &lt;script type="text/javascript"&gt;alert('hello');&lt;/script&gt; it was clear that arbitrary HTML was being bound. In this particular case our backend is scrubbing the possible output values, so I was able to move ahead using this solution.

     

     


  • 122

    Spent ~3 hours today writing Jasmine unit tests for a new AngularJS controller I made yesterday.


  • New Job, New Technologies

    Having grown pretty tired of the daily commute to New Jersey, I began looking for a job closer to my house in Philadelphia. I submitted an application to RJMetrics, and, after a pretty rigorous interview process, I was given a job offer. Which rules because they’re one of the best startups in Phila.

    So far I’ve been there almost four weeks. I’m being introduced to some web technologies like CoffeeScript and AngularJS. I’ve never done serious web programming before, but I’m glad to be getting into it. There are opportunities on the dev team to work with stuff like Clojure and Hadoop. I hope when I get a better feel for the front end that I can move into some of that.


  • AngularJS on Ubuntu VM

    Installing npm on Ubuntu

    I wanted to get started with this AngularJS tutorial, and I wanted to set up my environment to do so on an Ubuntu virtual machine.

    As I ran through the process of setting up the VM and installing some prerequisite stuff, I ran into a couple small issues. This post will explain what it takes to get up and running quickly.

    My host machine is a MacBook Pro running OS X 10.9.2. And I was installing Ubuntu 14.04 as the guest operating system (these instructions might be close to correct for Ubuntu 13.10 as well).

    Here’s what I did:

    • Downloaded and installed the latest version of VirtualBox
    • Downloaded the ISO image of the latest (14.04) 64-bit Ubuntu desktop OS
    • Opened VirtualBox and created a new virtual machine, pointing it to the Ubuntu image I just downloaded

    At this point I noticed that Ubuntu seemed to be laggy even though I’d given it 8GB of ram. This was remedied by enabling 3D acceleration and bumping up the memory allocated for the display. Note: the virtual machine has to be stopped before these settings can be edited.

    [caption id=”attachment_114” align=”alignnone” width=”1142”]Screenshot of VirtualBox display settings window Check the ‘Enable 3D Acceleration’ checkbox[/caption]

    I also installed the guest additions from the VirtualBox Device menu. After restarting the Guest OS I was able to run Ubuntu in full screen.

    Now I was ready to install Git, Nodejs, and npm.

    • To install git from the Terminal command line on Ubuntu: $ sudo apt-get install git
    • Clone the AngularJS git repository via the instructions in the tutorial
    • cd to the project directory
    • These instructions were helpful in getting Node.js installed.
    • To install Node.js: $ sudo apt-get install nodejs
    • As of writing, there is a naming conflict such that $ node does not refer to Node.js. The binary for Node.js is at /usr/bin/nodejs; a work-around is that you can create a symlink so the install scripts don't fail: $ sudo ln -s /usr/bin/nodejs /usr/bin/node
    • Install npm: $ sudo apt-get install npm
    • Now run $ npm start and you should see the tutorial's phonecat app if you open a browser in the guest OS and browse to http://localhost:8000/app/index.html