26

我正在尝试使用包含 Latex 样式方程式的 AngularJS 双向绑定文本。我想调用 MathJax 来格式化方程,但我不确定确保在 AngularJS 完成更改模型后调用 MathJax 的最佳方法。我想我需要一个回调。这是我的 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} \\)';

}

这是我的 HTML:

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

小提琴在这里:http: //jsfiddle.net/LukasHalim/UVjTD/1/。您会注意到,在小提琴上,即使您两次单击更新按钮,原始表达式也不会被删除 - 似乎是一个错误或冲突。

4

10 回答 10

36

在与 MathJax 战斗浪费了很多天(甚至可能是几周)之后,我非常熟悉它在动态更新数学表达式方面的各种怪癖。我是 Angular 的新手,但这给了我一个很好的潜入的机会,我最终找到了一个解决我的问题的解决方案——希望它也能解决你的问题。

现场演示:jsfiddle


我没有使用 Angular 提供的普通插值,而是创建了一个基于ng-bindcalled的新指令mathjax-bind

如果expression是一个包含数学代码的变量,那么\( {{expression}} \)您可以编写:

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

一切都会在适当的时候排版和更新。

该指令的支持代码如下:

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]]);
            });
        }]
    };
});
于 2013-05-20T09:42:24.950 回答
13

最简单、最快、最稳定的解决方案:

$rootScope.$watch(function(){
  MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
  return true;
});

好处:

  • 易于设置,只需复制此代码即可。
  • 您页面上的所有内容都已排版。
  • 它的渲染速度比其他解决方案快得多。这是因为它可以一次性渲染页面。这里的其他答案等待一项完成,直到他们排版下一项。如果有多个mathjax-bind指令(如另一个答案所示),这会使渲染变得非常缓慢。这就是我寻找不同答案的原因。
  • 您仍然可以使用 mathjax 设置中的“ignoreClass”选项轻松排除元素。

基准测试:100mathjax-bind条指令耗时63 秒,而使用此方法渲染页面需要1.5 秒。我知道这个函数会被执行很多次,因为它在每个摘要循环中都会被调用,但是,它并没有明显减慢页面的速度。

于 2016-11-14T13:41:40.370 回答
7

我创建了一个简单的小提琴,扩展了 Ben Alpert 的答案。这是fiddleplunk

具体来说,如果文本只有一部分要由 Mathjax 转换,则可以使用它。对于内联 mathjax,您必须用 $ 包围文本,而对于块显示,您必须用 $$ 包围块。(如果您创建相应的正则表达式,您可以使用任何您喜欢的格式)

应用程序.js

MathJax.Hub.Config({
    skipStartupTypeset: true,
    messageStyle: "none",
    "HTML-CSS": {
        showMathMenu: false
    }
});
MathJax.Hub.Configured();
var myApp = angular.module("myApp", []);
myApp.directive("mathjaxBind", function() {
    return {
        restrict: "A",
        scope:{
            text: "@mathjaxBind"
        },
        controller: ["$scope", "$element", "$attrs", function($scope, $element, $attrs) {
            $scope.$watch('text', function(value) {
                var $script = angular.element("<script type='math/tex'>")
                    .html(value == undefined ? "" : value);
                $element.html("");
                $element.append($script);
                MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $element[0]]);
            });
        }]
    };
});
myApp.directive('dynamic', function ($compile) {
  return {
    restrict: 'A',
    replace: true,
    link: function (scope, ele, attrs) {
      scope.$watch(attrs.dynamic, function(html) {
          html = html.replace(/\$\$([^$]+)\$\$/g, "<span class=\"blue\" mathjax-bind=\"$1\"></span>");
          html = html.replace(/\$([^$]+)\$/g, "<span class=\"red\" mathjax-bind=\"$1\"></span>");
        ele.html(html);
        $compile(ele.contents())(scope);
      });
    }
  };
});
function MyCtrl($scope, $element) {    
    $scope.html = "A coin of is $ \\frac{5}{4} $ thrown $$\\frac{1}{6}$$ dfv";
}

索引.html

<!DOCTYPE html>
<html ng-app="myApp">    
  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&delayStartupUntil=configured&dummy=.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.7/angular.js" data-semver="1.2.7"></script>
    <script src="app.js"></script>
  </head>
  <body>
  <div ng-controller="MyCtrl">
     <input type="text" ng-model="html"/><br/>
     <div dynamic="html"></div>
  </div>
</body>    

样式.css

input[type="text"] {
    width: 800px;
}
.red{
    color:red;
    display:inline-block;
}
.blue{
    color:blue;
    display:block;
}
于 2014-10-11T12:29:57.500 回答
2

这是一个指令,可让您在表达式中使用双卷曲标记(并且不需要在范围内设置表达式变量)。它基于这篇博文,除了我只支持 MathJax,我保存了编译后的 DOM,以便它更新范围变量的更改。

正如 Alex Osborn 所说,最好将非数学与数学分开。

用法:

<p>This is inline math: <latex>x^{ {{power}} }</latex>, 
and this is display math: <div latex> y^{ {{power}} } .</div></p>

在一个片段中:

angular.module('app', [])
  .controller('ctrl', function($scope) {
    $scope.power = "\\sin(x^2)";
  })
  .directive('latex', function() {
    return {
      restrict: 'AE',
      link: function(scope, element) {
        var newDom = element.clone();
        element.replaceWith(newDom);
        var pre = "\\(",
          post = "\\)";
        if (element[0].tagName === 'DIV') {
          pre = "\\[";
          post = "\\]";
        }
        scope.$watch(function() {
          return element.html();
        }, function() {
          console.log(element);
          newDom.html(pre + element.html() + post);
          MathJax.Hub.Typeset(newDom[0]);
        });
      }
    }
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>

<div ng-app="app" ng-controller="ctrl">
  <p>Power:
    <input ng-model="power" />
  </p>
  <p>This is the inline latex,
    <latex>x^{ {{power}} }</latex>, followed by some display mode latex
    <div latex>y^{ {{power}} } = {{power}}.</div>And that's it!
  </p>
</div>

于 2015-04-28T01:29:32.273 回答
2

一个简单的解决方案是使用 $timeout 将其放入MathJax.Hub.Queue(["Typeset", MathJax.Hub])浏览器事件队列(请参阅在 DOM 完成渲染后运行指令)。

像这样的东西:

            var app = angular.module('myApp', []);
            app.controller('myController', function ($scope, $timeout) {
                controller = this;

                $scope.Update = function () {
                    $scope.value = " \\( \\frac{5}{4} \\div \\frac{1}{6} \\)";
                    $timeout(controller.updateMathJax, 0);
                }

                this.updateMathJax = function () {
                    MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
                }
            });
于 2015-05-01T13:41:40.447 回答
1

看看http://jsfiddle.net/pz5Jc/

在您的模板中:

    {{Label}} <span id="mathElement">{{Expression}}</span>

在您的控制器中:

$scope.Update = function() {
    $scope.Expression = '\\frac{9}{4} \\div \\frac{1}{6}';
    $scope.Label = 'Updated Expression:'
    var math = MathJax.Hub.getAllJax("mathElement")[0];
    math.Text('\\frac{4}{4} \\div \\frac{2}{6}');
}

几点:

我对mathjax不太熟悉,但是:

  • 从表达式中分离出标签允许您直接使用表达式。
  • 您需要手动选择一个 DOM 元素来强制刷新表达式。不幸的是,这不是一种非常“有角度”的做事方式 - 但是当 mathjax 解析表达式(并插入它自己的 DOM 元素)时,它会将这些元素推到角度绑定之外。
  • 此处的修复是专门选择正确的 mathjax 元素并调用文本更改函数来更新表达式。
于 2013-04-18T21:23:00.020 回答
1

您可以尝试使用我的修改http://jsfiddle.net/bmma8/4/ 修改输入或单击按钮将更新您的表达式。

js:

MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX","output/HTML-CSS"],
    tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}
});

var myApp = angular.module('myApp',[]);


function MyCtrl($scope, $log) {

    var QUEUE = MathJax.Hub.queue;  // shorthand for the queue

    $scope.Update = function() {
        QUEUE.Push(["Text",MathJax.Hub.getAllJax("MathOutput")[0],"\\displaystyle{"+ $scope.Expression+"}"]);
        //$scope.Expression = 'Updated Expression: \\( \\frac{9}{4} \\div \\frac{1}{6} \\)';
        //MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
    }
    $scope.Expression = 'Original Expression: \\( \\frac{5}{4} \\div \\fra

和html:

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

         <input ng-model="Expression" ng-change="Update()">
         <div id="MathOutput">
         You typed: ${}$
         </div>
 </div>

亚历山大

于 2013-04-18T21:54:04.447 回答
0

我实际上想到了另一种解决方案。当你渲染一些角度和数学时,你会这样做:

角度控制器

$scope x = 5;

HTML

<h3> {{ '$ Multiplication = '+ x + ' * 2 =' + (x*2) + '$'}} </h3>

格式化数学 Jax 结果

乘法 = 5 * 2 = 10

关键是将括号内的美元符号作为文本包含在内。当 Angular 渲染它们时,美元符号将显示为纯文本,但当 Math Jax 格式开始起作用时,它会识别美元符号并发挥作用。

于 2014-05-27T17:20:55.533 回答
0

我为此建立了一个指令....

小提琴:http : //jsfiddle.net/8YkUS/1/

HTML

p data-math-exp data-value="math">

JAVASCRIPT

 appFlipped.directive("mathExp", function () {
    return {
        scope: {
            value: "="
        },
        link: function (scope, el) {

            var domEl = el[0];
            scope.$watch("value", function (newValue) {

                //nothing to do here
                if (newValue == null || window.MathJax == null)return;

                //update the dom with the new value and pass the hub for styling the equation
                domEl.innerHTML = '`' + newValue + '`';
                MathJax.Hub.Queue(["Typeset", MathJax.Hub, domEl]);
            });
        }
    }
});
于 2014-07-03T05:38:47.780 回答
0

我对罗尼的解决方案做了更多的修改。显示数学应以显示模式显示;和

<script type="math/tex; mode=display">

我在生成的 span 中添加了一个属性来表明这一点。

小提琴在这里http://jsfiddle.net/repa/aheujhfq/8/

于 2015-02-12T21:13:56.443 回答