Lukas Halim Lukas Halim - 26 days ago 10
AngularJS Question

Getting MathJax to update after changes to AngularJS model

I am trying to use AngularJS two-way binding text which includes Latex style equations. I would like to call MathJax to format the equations, but I'm not sure of the best way to ensure that MathJax is called after AngularJS finishes changing the model. I think I need a callback. Here is my JavaScript:

var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.Update = function() {
$scope.Expression = 'Evaluate: \\( \\frac{9}{4} \\div \\frac{1}{6} \\)';
MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
}
$scope.Expression = 'Evaluate: \\( \\frac{5}{4} \\div \\frac{1}{6} \\)';


}

And here is my HTML:

<div ng-controller="MyCtrl">
<button ng-click="Update()">Update</button>
{{Expression}}
</div>


Fiddle is here: http://jsfiddle.net/LukasHalim/UVjTD/1/. You'll notice that on the fiddle the original expression isn't removed even after you click the update button twice - seems like a bug or conflict.

Answer

Having wasted many days (and maybe weeks) fighting MathJax, I'm all too familiar with its various quirks with updating math expressions on the fly. I'm brand new to Angular but this gave me a good chance to dive in and I ended up with a solution which solves my problems -- hopefully it'll solve yours as well.

Live demo: http://jsfiddle.net/spicyj/YpqVp/


Instead of using the plain interpolation that Angular provides, I created a new directive based on ng-bind called mathjax-bind.

If expression is a variable containing math code, then instead of \( {{expression}} \) you can write:

<span mathjax-bind="expression"></span>

and everything will be typeset and updated at the appropriate times.

The supporting code for the directive follows:

myApp.directive("mathjaxBind", function() {
    return {
        restrict: "A",
        controller: ["$scope", "$element", "$attrs",
                function($scope, $element, $attrs) {
            $scope.$watch($attrs.mathjaxBind, function(texExpression) {
                var texScript = angular.element("<script type='math/tex'>")
                    .html(texExpression ? texExpression :  "");
                $element.html("");
                $element.append(texScript);
                MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $element[0]]);
            });
        }]
    };
});