web-dev-qa-db-ja.com

AngularJSモデルの変更後にMathJaxを更新する

ラテックススタイルの方程式を含む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/ 。フィドルでは、更新ボタンを2回クリックしても元の式が削除されないことに気付くでしょう。これは、バグまたは競合のようです。

26
Lukas Halim

MathJaxとの戦いで何日も(そしておそらく何週間も)無駄になってきた私は、その場で数式を更新するというさまざまな癖に精通し​​ています。私はAngularに慣れていないのですが、これは私に飛び込む良い機会を与えてくれました、そして私は私の問題を解決する解決策に行き着きました-うまくいけばそれはあなたの問題も解決するでしょう。

ライブデモ: jsfiddle


Angularが提供する単純な補間を使用する代わりに、 _ng-bind_ に基づいて_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]]);
            });
        }]
    };
});
_
36
Sophie Alpert

最も単純で、最も速く、最も安定したソリューション:

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

利点:

  • セットアップは簡単で、このコードをコピーするだけです。
  • ページ上のすべてがタイプセットされています。
  • 他のソリューションよりもはるかに高速にレンダリングされます。これは、ページを一度にレンダリングできるためです。ここでの他の回答は、次のアイテムをタイプセットするまで、1つのアイテムが終了するのを待ちます。たとえば、複数のmathjax-bindディレクティブがある場合(別の回答が示唆しているように)、レンダリングが遅くなります。この点が私が別の答えを探していた理由です。
  • Mathjax設定でオプション「ignoreClass」を使用すると、要素を簡単に除外できます。

ベンチマーク:100 mathjax-bindディレクティブは63秒かかりましたが、このメソッドではページのレンダリングに1.5秒かかりました。この関数はダイジェストサイクルごとに呼び出されるため、頻繁に実行されることはわかっていますが、ページの速度が著しく低下することはありません。

13
user5210222

ベン・アルパートの答えを拡張した簡単なフィドルを作成しました。これが フィドルプランク です。

具体的には、テキストにMathjaxによって変換されるテキストの一部しかない場合は、これを使用できます。インラインmathjaxの場合は、テキストを$で囲む必要があり、ブロック表示の場合は、ブロックを$$で囲む必要があります。 (対応する正規表現を作成する場合は、任意の形式を使用できます)

app.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";
}

index.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="[email protected]" 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>    

style.css

input[type="text"] {
    width: 800px;
}
.red{
    color:red;
    display:inline-block;
}
.blue{
    color:blue;
    display:block;
}
7
Roney Island

これは、式内でダブルカーリーマークアップを使用できるようにするディレクティブです(スコープに式変数を設定する必要はありません)。これは このブログ投稿 に基づいていますが、MathJaxのみをサポートし、コンパイルされたDOMを保存して、スコープ変数の変更時に更新されるようにします。

アレックスオズボーンが言ったように、非数学を数学から分離するのが最善です。

使用法:

<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>
2
jladan

簡単な解決策は、$ 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]);
                }
            });
2

私の変更を試すことができます 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>

アレクサンドル

1
heralight

見てください 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要素を挿入する)と、それらの要素をangularバインディングの外にプッシュします。
  • ここでの修正は、正しいmathjax要素を具体的に選択し、テキスト変更関数を呼び出して式を更新することです。
1
Alex Osborn

私は実際に別の解決策を考えました。いくつかのangularと数学をレンダリングすると、次のようになります。

角度コントローラー

$scope x = 5;

HTML

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

フォーマットされたMathJaxの結果

乗算= 5 * 2 = 1

重要なのは、括弧内にドル記号をテキストとして含めることです。 Angularがそれらをレンダリングすると、ドル記号はプレーンテキストとして表示されますが、Math Jax形式が実行されると、ドル記号を認識して魔法を実行します。

0
Avi

このためのディレクティブを作成します。

フィドル: 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]);
            });
        }
    }
});
0
Jose Ch.

私はロニーの解決策をもう少しいじった。表示計算は表示モードで表示する必要があります。と

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

生成されたスパンに属性を追加して、それを示しました。

フィドルはここにあります http://jsfiddle.net/repa/aheujhfq/8/

0
user3021380