[Handlebars]textareaやpreタグで改行後にスペースが入ってしまう[NestJS]
使用バージョン
- nestjs@10.3.10
- handlebars@4.7.7
- hbs@4.2.0
こちらのリポジトリで動作を試すことができます。
現象
controllerで改行入りの文字をviewに渡す。
import { Controller, Get, Render } from '@nestjs/common';
@Controller()
export class AppController {
@Get()
@Render('index')
getHello() {
const message = `あいうえお\n改行されました`;
return { message };
}
}
handlebarsのInline Partialsを使う。
<div class="content">
{{> content}}
</div>
{{#> layout}}
{{#*inline "content"}}
<div>
二重括弧{{message}}
</div>
<div>
三重括弧{{{message}}}
</div>
<div>
<textarea>{{message}}</textarea>
</div>
<div>
<textarea>{{{message}}}</textarea>
</div>
{{/inline}}
{{/layout}}
textarea内とpreタグ内の改行後に謎の半角スペースが2つ入りました。
原因
https://github.com/handlebars-lang/handlebars.js/issues/858 ここで色々書かれていますが、
Partials呼び出し部分のインデントが自動的にPartialsの出力にも反映されます。
それにより、textareaやpreタグが影響を受けるようです。
<div class="content">
以下のPartials呼び出し部分のインデントのこと
{{> content}}
</div>
このインデントがHTMLでの表示時にtaxtareやpreタグ内でスペースとして反映されていたということ。
この自動インデントは以下によるとmustache
の仕様に合わせるために導入されたようです。
解決案
案1
preventIndent
オプションを使う。
Handlebarsで自動インデントを無効にするオプションを用意してくれています。
このオプションはHandlebarsのコンパイル時に適用され、Partialsの内容にインデントを追加しないようにします。
Handlebarasのコードを見てみると、
どうやらここでpreventIndent
オプションをチェックして、インデントを調整しているのが伺えます。
ただ肝心の、NestJSでどうやってpreventIndent
オプションを有効にするかは見つけられず。
NestJSのデフォルト設定では直接このオプションを指定する方法がなさそうなので、以下のようにHandlebarsのcompile()
をカスタマイズする方法を考えました。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as hbs from 'hbs';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');
hbs.registerPartials(join(__dirname, '..', 'views', 'layouts'));
// 元の関数を保持
const originalCompile = hbs.handlebars.compile;
hbs.handlebars.compile = (input, options) => {
const newOptions = options || {};
// ここでpreventIndentを有効に
newOptions.preventIndent = true;
return originalCompile.call(this, input, newOptions);
};
await app.listen(3000);
}
bootstrap();
ここで、オーバーライドしてるcompile()
とは以下のことです。
これにより、textareaやpreタグでの改行後のスペースが無くなりました。
ちなみに、
hbs
でなくexpress-handlebars
を使っている場合は、以下のようにcompilerOptions
でpreventIndent
オプションを設定できそうです。
案2
Partials呼び出し部分にインデントを用いない。
<div class="content">
// Partials呼び出し部分でインデントを用いない
{{> content}}
</div>
すると、半角スペースが消えました。
局所的に挙動を変えたい時はこちらでもいいかも...?
ただ、将来的にtextareaやpreタグを使用する箇所が増えたときに、スペースを反映させたくない所全てに適用する必要があり漏れが発生しそうだったり、
エディタによる自動整形でインデントが自動で入る場合に、対象のファイルだけ入らないようにする必要も出ててきたり...
グローバルに設定しておくほう安定かもですね。
おわり
この記事が少しでも参考になれば幸いです。
励みになりますのでもしよかったらいいねお願いします🙏
Discussion