Подтвердить что ты не робот

Получение MathJax для обновления после изменений в модели AngularJS

Я пытаюсь использовать текст двустороннего связывания AngularJS, который включает в себя уравнения стиля латекса. Я хотел бы назвать MathJax для форматирования уравнений, но я не уверен в том, что MathJax вызывается после того, как AngularJS закончит изменение модели. Я думаю, мне нужен обратный вызов. Вот мой 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>

Fiddle здесь: http://jsfiddle.net/LukasHalim/UVjTD/1/. Вы заметите, что на скрипке исходное выражение не удаляется даже после нажатия кнопки обновления дважды - кажется ошибкой или конфликтом.

4b9b3361

Ответ 1

Потратив много дней (и, может быть, недель) на борьбу с 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]]);
            });
        }]
    };
});

Ответ 2

Простейшее, быстрое и стабильное решение:

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

<сильные > Преимущества:

  • Простота настройки, просто скопируйте этот код.
  • Все на вашей странице набирается.
  • Он работает намного быстрее, чем другие решения. Это связано с тем, что он может отображать страницу за один раз. Другие ответы здесь ждут завершения одного элемента, пока они не наберут следующий. Это делает рендеринг рендеринга медленным, если существуют, например, несколько директив mathjax-bind (как предлагает другой ответ). Этот момент является причиной того, что я искал другой ответ.
  • Вы можете легко исключить элементы, используя опцию "ignoreClass" в настройках mathjax.

Бенчмаркинг: 100 mathjax-bind приняли 63 секунды, в то время как для этого метода потребовалось 1,5 секунды. Я знаю, что эта функция будет выполняться много, поскольку она вызвала каждый цикл дайджеста, однако она заметно не замедляет страницу.

Ответ 3

Я создал простую скрипку, расширяющуюся на ответ Бен Алперта. Здесь fiddle и plunk.

В частности, если текст имеет только часть его, который должен быть преобразован 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;
}

Ответ 4

Здесь указатель, который позволяет использовать двойную курсивную разметку внутри выражения (и не требует установки переменной выражения в области). Это основано на этом сообщении в блоге, за исключением того, что я поддерживаю только 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 it!
  </p>
</div>

Ответ 5

Простым решением является использование $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]);
                }
            });

Ответ 6

Взгляните на 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, чтобы принудительно обновить выражение. Это не очень "w500" способ сделать что-то несчастливо, но когда mathjax анализирует выражение (и вставляет его собственные элементы DOM), он толкает эти элементы вне привязок angular.
  • Исправлено здесь, чтобы выбрать правильный элемент mathjax и вызвать функцию изменения текста для обновления выражения.

Ответ 7

Вы можете попробовать с моими изменениями 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>

Александр

Ответ 8

Я действительно думал о другом решении. Когда вы выполняете некоторые angular и математику, вы делаете это:

ANGULAR КОНТРОЛЛЕР

$scope x = 5;

HTML

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

Формированный результат Math Jax

Умножение = 5 * 2 = 10

Ключ должен включать знаки доллара в скобках в виде текста. Когда angular отображает их, знаки доллара будут отображаться как обычный текст, но когда вступает в действие формат Math Jax, он распознает знаки доллара и совершит магию.

Ответ 9

Я строю директиву для этого....

FIDDLE: 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]);
            });
        }
    }
});

Ответ 10

Я немного поработал над решением Roney. Отображение математики должно отображаться в режиме отображения; с

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

Я добавил атрибут сгенерированного диапазона, чтобы указать это.

Fiddle здесь http://jsfiddle.net/repa/aheujhfq/8/