MathJax в Angular 6? - PullRequest
       30

MathJax в Angular 6?

3 голосов
/ 12 марта 2019

К сожалению, очень мало информации об этой библиотеке. После установки мне не совсем понятно, что мне нужно импортировать в app.module.ts и есть ли там что-то для импорта? Я прописал следующий код в index.html:

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  tex2jax: {
    inlineMath: [['$','$'], ['\\(','\\)']]
  }
});
</script>
<script type="text/javascript" async
 src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js? 
 config=TeX-MML-AM_CHTML'>
</script>

А как я могу применить MathJax, если у меня есть не простой текст, а таблица, в которой текст с формулами появляется в некоторых столбцах? Возможно, вы можете каким-то образом перенести всю таблицу в MathJax.Hub.Queue?

1 Ответ

5 голосов
/ 14 марта 2019

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

Шаг первый

Добавьте @types/mathjax зависимость к вашему package.json файлу.

Шаг второй

Добавьте недавно добавленный тип к tsconfig.app.json, например:

{
  "compilerOptions": {
    "types": ["mathjax"]
  }
}

Это позволит вам использовать такие конструкции, как MathJax.Callback.Queue, и ваша IDE не будет жаловаться на неизвестностьвведите и т. д.

Шаг третий: создайте объект-обертку для вашего математического содержимого (необязательно)

У меня возникли некоторые проблемы с Mathml, поэтому я создал обертку для математики, которая выглядит следующим образом:

export interface MathContent {
  latex?: string;
  mathml?: string;
}

Шаг четвертый

Теперь нам нужно определить модуль, который будет вводить тег сценария MathJax с настройкой.Поскольку он будет загружаться динамически async, мы должны убедиться, что набор текста не начинается до полной загрузки MathJax.Самый простой способ - сохранить Observer<any> в window объекте.Observer и функции рендеринга могут быть включены в сервис.

// see https://stackoverflow.com/a/12709880/1203690
declare global {
  interface Window {
    hubReady: Observer<boolean>;
  }
}

@Injectable()
export class MathServiceImpl {
  private readonly notifier: ReplaySubject<boolean>;

  constructor() {
    this.notifier = new ReplaySubject<boolean>();
    window.hubReady = this.notifier; // as said, bind to window object
  }

  ready(): Observable<boolean> {
    return this.notifier;
  }

  render(element: HTMLElement, math?: MathContent): void {
    if (math) {
      if (math.latex) {
        element.innerText = math.latex;
      } else {
        element.innerHTML = math.mathml;
      }
    }

    MathJax.Hub.Queue(['Typeset', MathJax.Hub, element]);
  }
}

Шаг пятый

Теперь мы создадим директиву, которая будет запускать рендеринг после загрузки MathJax.Директива может выглядеть так:

@Directive({
  selector: '[appMath]'
})
export class MathDirective implements OnInit, OnChanges, OnDestroy {
  private alive$ = new Subject<boolean>();

  @Input()
  private appMath: MathContent;
  private readonly _el: HTMLElement;

  constructor(private service: MathServiceImpl,
              private el: ElementRef) {
    this._el = el.nativeElement as HTMLElement;
  }

  ngOnInit(): void {
    this.service
      .ready()
      .pipe(
        take(1),
        takeUntil(this.alive$)
      ).subscribe(res => {
        this.service.render(this._el, this.appMath);
    });
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log(changes);
  }

  ngOnDestroy(): void {
    this.alive$.next(false);
  }
}

Шаг шестой (почти там)

На четвертом шаге я упомянул async loading.Согласно документации по MathJax это делается с использованием document.createElement.Угловой модуль - идеальное место для этой логики.Чтобы вызвать метод .ready() на нашем MathService, мы будем использовать MathJax.Hub.Register.StartupHook и передать наблюдаемый от MathService Таким образом, наш модуль будет выглядеть следующим образом:


@NgModule({
  declarations: [MathDirective],
  exports: [MathDirective]
})
export class MathModule {
  constructor(mathService: MathServiceImpl) {
    // see https://docs.mathjax.org/en/latest/advanced/dynamic.html
    const script = document.createElement('script') as HTMLScriptElement;
    script.type = 'text/javascript';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML';
    script.async = true;

    document.getElementsByTagName('head')[0].appendChild(script);

    const config = document.createElement('script') as HTMLScriptElement;
    config.type = 'text/x-mathjax-config';
    // register notifier to StartupHook and trigger .next() for all subscribers
    config.text = `
    MathJax.Hub.Config({
        skipStartupTypeset: true,
        tex2jax: { inlineMath: [["$", "$"]],displayMath:[["$$", "$$"]] }
      });
      MathJax.Hub.Register.StartupHook('End', () => {
        window.hubReady.next();
        window.hubReady.complete();
      });
    `;

    document.getElementsByTagName('head')[0].appendChild(config);
  }

  // this is needed so service constructor which will bind
  // notifier to window object before module constructor is called
  public static forRoot(): ModuleWithProviders {
    return {
      ngModule: MathModule,
      providers: [{provide: MathServiceImpl, useClass: MathServiceImpl}]
    };
  }
}

Шаг седьмой: рендеринг математики

Теперь все готово, просто импортируйте MathModule.forRoot() в модуль, где вы хотите сделать математику.Компонент будет выглядеть следующим образом:

export class AppComponent {
  mathLatex: MathContent = {
    latex: 'When $a \\ne 0$, there are two solutions to $\\frac{5}{9}$'
  };

  mathMl: MathContent = {
    mathml: `<math xmlns="http://www.w3.org/1998/Math/MathML">
  <mrow>
    <mover>
      <munder>
        <mo>∫</mo>
        <mn>0</mn>
      </munder>
      <mi>∞</mi>
    </mover>
    <mtext> versus </mtext>
    <munderover>
      <mo>∫</mo>
      <mn>0</mn>
      <mi>∞</mi>
    </munderover>
  </mrow>
</math>`
  };
}

и шаблон

<div [appMath]="mathLatex"></div>

<div [appMath]="mathMl"></div>

<!-- will render inline element math -->
<div [appMath]>
  $E = mc^2$
</div>

, которые должны отображаться для этого

enter image description here

Шаг восьмой (бонус)

Вот пример рабочего стекаблика https://stackblitz.com/edit/mathjax-example, чтобы вы могли проверить свой прогресс в реализации

...