Embedded AngularJS partials, or: HTML5 !== XHTML

I just bumped heads with a really gnarly problem. In fact it’s a miracle that I stumbled upon the solution, it could have gone unseen for a long time!

The root of the problem was that I used XHTML syntax in a HTML5 document, and I didn’t get what I expected. I’m still getting used to HTML5 and my understanding was that HTML5 was a superset of HTML4 and XHTML with some extensions. As such I expected XHTML syntax to be supported by HTML5 — but, not so!

I was trying to refactor my AngularJS app to use partial views that were embedded in <script> tags with the type ‘text/ng-template’ and an associated ID.

On my way I found this article on StackOverflow which said:

make sure that the inline script tags are children of the element that has the ng-app=”myApp” attribute.

That was actually a problem I had to solve, I think, because I’d put the ng-app directive on the <html> element not the <body> element. But things still weren’t working and my code looked like this:

...
  <body ng-app="fflurry_app" ng-controller="MainCtrl">
    <ul class="menu">
      <li><a href="#/home">Home</a></li>
    </ul>
    <div ng-view />
    <!-- partials: -->
<script type="text/ng-template" id="home.html">
<h1>Welcome!</h1>
</script>
<script type="application/javascript">
'use strict';
var fflurry_app = angular.module( 'fflurry_app', [] ).
  config( [ '$routeProvider', function( $routeProvider ) {
      $routeProvider.when( '/home', { templateUrl: 'home.html', controller: 'MainCtrl' } );
      // ...      
    }]);
    // ...
</script>
...
</body></html>

So… have you spotted the problem? It’s a tricky one… the problem is with the ‘<div ng-view />’ element. This is being parsed as an open <div> with no closing </div> in HTML5. In XHTML this would be an opened and closed element. In HTML5 it’s ignoring the ‘/>’ and just opening a <div>! Hah! So the <script> partials are children on this <div> and not of the ng-app container, so they’re not being found by Angular!

The solution is now trivial. Just make sure we open and close our ng-view <div> like this: <div ng-view></div>.

Shell scripting for archive restoration

I’ve been restoring my archives. Basically I have a bit over 1.3TB of data that I’ve tarballed up and stashed on some disconnected SATA disks, and now that I have a computer with the capacity to hold all that data I’m resurrecting the file shares from the archived tarballs. You can see my restore script here:

restore.sh

#!/bin/bash
cd "`dirname $0`"
data_path=/var/sata2/data
tar xf $data_path/1999.tar.gz --hard-dereference >  output.txt 2>&1
tar xf $data_path/2001.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2002.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2003.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2004.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2005.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2006.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2007.tar.gz --hard-dereference >> output.txt 2>&1
tar xf $data_path/2008.tar.gz --hard-dereference >> output.txt 2>&1

The restore.sh script creates an output.txt file that lists any errors from tar during the restore process. I then have a set of scripts that process this output.txt file fixing up two types of common errors.

Fixing dates

The first error is that the date of the file in the archive isn’t a reasonable value. For example, I had files reporting modification time somewhere back in 1911, before computers. To fix the dates with this problem I run the following scripts:

fix-dates

#!/bin/bash
cd "`dirname $0`";
./bad-date | xargs -0 touch --no-create

bad-date

#!/bin/bash
awk -f bad-date.awk < output.txt | while read line
do
  # note: both -n and \c achieve the same end.
  echo -n -e "$line\0\c"
done

bad-date.awk

{
  if ( /tar: ([^:]*): implausibly old time stamp/ ) {
    split( $0, array, ":" )
    filepath = array[ 2 ]
    sub( / /, "", filepath )
    printf( "%s\n", filepath )
  }
}

Fixing hard links

The second class of error that I can receive is that the file that is being extracted from the archive is a hard link to an already existing file, but the hard link cannot be created because the number of links to the target has reached its limit. I think I used ReiserFS as my file system the archives were on originally, and I’m using Ext4 now. Ext4 seems to have limitations that ReiserFS didn’t. Anyway, it’s not big deal, because I can just copy the target to the path that failed to link. This creates a duplicate file, but that’s not a great concern. I’ll try to fix up such duplicates with my pcdedupe project.

fix-links

#!/bin/bash
cd "`dirname $0`";
./bad-link | xargs -0 ./fix-link

bad-link

#!/bin/bash
awk -f bad-link.awk < output.txt | while read line
do
  # note: both -n and \c achieve the same end.
  echo -n -e "$line\0\c"
done

bad-link.awk

{
  if ( /tar: ([^:]*): Cannot hard link to `([^']*)': Too many links/ ) {
    split( $0, array, ":" )
    linkpath = array[ 2 ]
    sub( / /, "", linkpath )
    filepath = array[ 3 ]
    sub( / Cannot hard link to `/, "", filepath )
    filepath = substr( filepath, 0, length( filepath ) )
    printf( "%s:%s\n", filepath, linkpath )
  }
}

fix-link

#!/bin/bash
cd "`dirname $0`";
spec="$1"
file=`echo "$spec" | sed 's/\([^:]*\):.*/\1/'`
link=`echo "$spec" | sed 's/[^:]*:\(.*\)/\1/'`
#echo "$spec"
#echo Linking "'""$link""'" to "'""$file""'"...
#echo ""
if [ ! -f "$file" ]; then
  echo Missing "'""$file""'"...
  exit 1;
fi
cp "$file" "$link"

check-output

I then checked for anything that I’d missed with my scripts with the following:

#!/bin/bash
cd "`dirname $0`";
cat output.txt | grep -v "Cannot hard link" | grep -v "implausibly old time"