Angular 6中的MathJax?

问题描述 投票:2回答:1

不幸的是,关于这个库的信息非常少。安装完成后我还不完全清楚我需要导入到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?

angular angular6 mathjax
1个回答
5
投票

我在两个星期前看到了同样的问题,今天我终于设法让它发挥作用。我不是角度专家所以它可能需要一些优化,但核心功能正在发挥作用。

Step one

@types/mathjax依赖项添加到您的package.json文件中。

Step two

将新添加的类型添加到tsconfig.app.json,如下所示:

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

这将允许您使用MathJax.Callback.Queue等构造,您的IDE不会抱怨未知类型等。

Step three create wrapper object for your math content (optional)

我在Mathml中遇到了一些问题,所以我创建了数学包装,如下所示:

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

Step four

现在我们需要定义哪些模块可以通过配置注入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]);
  }
}

Step five

现在我们将创建指令,该指令将在加载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);
  }
}

Step six (almost there)

在第四步我提到async装载。根据onMathJax的文档,这是使用document.createElement完成的。角度模块是这种逻辑的理想之选。要在我们的.ready()上触发MathService方法,我们将使用MathJax.Hub.Register.StartupHook并从MathService传递observable所以我们的模块将如下所示:


@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}]
    };
  }
}

Step seven: render math

现在一切准备就绪,只需在你要渲染数学的模块中导入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

Step eight (bonus)

这是工作stackblitz示例https://stackblitz.com/edit/mathjax-example所以你可以检查你的进度与实现

© www.soinside.com 2019 - 2024. All rights reserved.