1

I am using Angular UI Router with MathJax. When I route to a new state, the MathJax does not get processed. What burns my noodle is that if I have an update button with exactly the same code it works. Just not by itself.

PLUNKER

HTML

<p id="MathExample"><strong>Equation:</strong> {{data.equation}}</p>
<button class="btn btn-primary" ng-click="update()">Update</button>

JS

$stateProvider.state('route1', {
  url: "/route1",
  templateUrl: "route1.html",
  controller: function($scope, $http){

    $http({method: 'GET', url: 'data.json'})
    .success(function(data) {
      $scope.data = data;
       MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
       //You would think that the value would be updated here!
    })

    $scope.update = function($scope){
      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
      //This works just fine, but is not automatic.
    }

  }
 });

I'm trying to figure out why this is the case. Do I need to use $apply? One question hinted at the use of a directive. However, I don't need this function. I really just need the JSON data with the math expressions to reliably become typeset.

Community
  • 1
  • 1
Diode Dan
  • 4,801
  • 6
  • 25
  • 34

2 Answers2

3

It looks like you just need to wait for the DOM to settle down before MathJax can process it. A simple setTimeout call seems to fix the issue:

$http({method: 'GET', url: 'data.json'})
.success(function(data) {
  $scope.data = data;
  setTimeout(function() {
    MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
  });
});

See http://plnkr.co/edit/xIVvUHntVZ7lftGz9cDn?p=preview for an example.


You mentioned using a directive; you could use one to abstract away the need to call the MathJax function manually at all.

In this example, I've created a directive called math that automatically lays out whatever's passed in with MathJax:

var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){

  $urlRouterProvider.otherwise("/");//Was defaulting to route 1. No more

  $stateProvider.state('route1', {
      url: "/route1",
      templateUrl: "route1.html",
      controller: function($scope, $http, $timeout){
        $http({method: 'GET', url: 'data.json'})
        .success(function(data) {
          $scope.data = data;
        });
      }
  });

});

myapp.directive('math', function() {
  return {
    restrict: 'EA',
    scope: {
      math: '@'
    },
    link: function(scope, elem, attrs) {
      scope.$watch('math', function(value) {
        if (!value) return;
        elem.html(value);
        MathJax.Hub.Queue(["Typeset", MathJax.Hub, elem[0]]);
      });
    }
  };
});
<hr>
<h3>Route 1's data</h3>
<p>
  <strong>Name:</strong>
  {{data.name}}
</p>
<p>
  <strong>Equation:</strong>
  <span math="{{data.equation}}"></span>
</p>

See http://plnkr.co/edit/c9NBXXd9kF7Jne6saS3i?p=preview for a demonstration.

Going this route, absolutely anything passed to the math directive will be automatically rendered with MathJax whenever it changes. See this demo for an example that lets you edit the equation: http://plnkr.co/edit/dJSEGtBKSrKLjiwuLsl5?p=preview

Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • Thank you, thank you, thank you. I appreciate the modified Plunker. I figured it had something to do with the rendering timing. I don't understand the timeout though. There is no set time period. Is this some kind of Angular hack? Or are you saying: "when this success function is completed, do the following"? – Diode Dan Jan 21 '14 at 04:30
  • `setTimeout` defaults to a delay of `0`. You don't need to use Angular's `$timeout` service, as you don't need a digest cycle for MathJax to work. I've also updated the answer with some more information, including how you might go about building a directive. – Michelle Tilley Jan 21 '14 at 04:34
  • Great answer. Very thorough. The directive is an interesting twist. – Diode Dan Jan 21 '14 at 22:05
1

Because you are trying to modify Math on the page before it is rendered. You can use the $timeout to wait until it is rendered:

var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){

  $urlRouterProvider.otherwise("/");//Was defaulting to route 1. No more

  $stateProvider.state('route1', {
      url: "/route1",
      templateUrl: "route1.html",
      controller: function($scope, $http, $timeout){
        $http({method: 'GET', url: 'data.json'})
        .success(function(data) {
          $scope.data = data;
          $timeout(function() {
            MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
          }, 0);
        })
        $scope.update = function($scope){
          MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
        }
      }
  });
});
Sanghyun Lee
  • 21,644
  • 19
  • 100
  • 126