Handlebars.js: 動的部分ブロックのサポート

作成日 2017年08月11日  ·  11コメント  ·  ソース: handlebars-lang/handlebars.js

現在、ブロック構文を使用して動的パーシャルを定義する方法はないため、サブ式が未定義のパーシャルに解決された場合にフェイルオーバーを提供できます。

何でこれが大切ですか?

動的部分ブロックを使用する場合、モデル構築コードは、どの部分ブロックが登録されているかを認識しない場合があります。 たとえば、エラーコードに対応する名前でパーシャルを登録して、場合によってはエラー固有のテンプレートをレンダリングし、デフォルトのテンプレートにフォールバックできるようにすることができます。 したがって、動的パーシャルが登録済みパーシャルに解決されない場合に使用するデフォルトのテンプレートを提供すると便利です。

closeBlock helperNameがオプションの場合、この動作をサポートするのはかなり簡単であることがわかりました。

diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js
index 3c1a38f..f6295d8 100644
--- a/lib/handlebars/compiler/helpers.js
+++ b/lib/handlebars/compiler/helpers.js
@@ -1,9 +1,11 @@
 import Exception from '../exception';

 function validateClose(open, close) {
-  close = close.path ? close.path.original : close;
+  if (close.hasOwnProperty('path')) {
+    close = close.path ? close.path.original : close.path;
+  }

-  if (open.path.original !== close) {
+  if (close && open.path.original !== close) {
     let errorNode = {loc: open.path.loc};

     throw new Exception(open.path.original + " doesn't match " + close, errorNode);
diff --git a/spec/partials.js b/spec/partials.js
index 266837d..7982677 100644
--- a/spec/partials.js
+++ b/spec/partials.js
@@ -19,6 +19,20 @@ describe('partials', function() {
     shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
     shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
   });
+
+  it('dynamic partials with failover', function() {
+    var string = 'Dudes: {{#dudes}}{{#> (partial)}}Anonymous {{/}}{{/dudes}}';
+    var partial = '{{name}} ({{url}}) ';
+    var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
+    var helpers = {
+      partial: function() {
+        return 'missing';
+      }
+    };
+    shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Anonymous Anonymous ');
+    shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Anonymous Anonymous ');
+  });
+
   it('failing dynamic partials', function() {
     var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
     var partial = '{{name}} ({{url}}) ';
diff --git a/src/handlebars.yy b/src/handlebars.yy
index ce06498..1df03cf 100644
--- a/src/handlebars.yy
+++ b/src/handlebars.yy
@@ -79,7 +79,7 @@ inverseChain
   ;

 closeBlock
-  : OPEN_ENDBLOCK helperName CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
+  : OPEN_ENDBLOCK helperName? CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
   ;

 mustache

もちろん、このソリューションでは、 {{/}}でブロックを閉じることができます。これは、通常の方法としてはお勧めできませんが、暗黙的に何も害することはありません。 validateCloseは、 open.type === 'SubExpression'場合にのみ空のクローズを許可する追加のチェックを含めることができるため、動的な部分ブロックのみが空のクローズを許可されます(オープンがタイプSubExpression )。

別のオプションは、 closeBlock partialNameを許可することです。

diff --git a/spec/partials.js b/spec/partials.js
index 266837d..4800d3f 100644
--- a/spec/partials.js
+++ b/spec/partials.js
@@ -19,6 +19,20 @@ describe('partials', function() {
     shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
     shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
   });
+
+  it('dynamic partials with failover', function() {
+    var string = 'Dudes: {{#dudes}}{{#> (partial)}}Anonymous {{/(partial)}}{{/dudes}}';
+    var partial = '{{name}} ({{url}}) ';
+    var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
+    var helpers = {
+      partial: function() {
+        return 'missing';
+      }
+    };
+    shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Anonymous Anonymous ');
+    shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Anonymous Anonymous ');
+  });
+
   it('failing dynamic partials', function() {
     var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
     var partial = '{{name}} ({{url}}) ';
diff --git a/src/handlebars.yy b/src/handlebars.yy
index ce06498..2d63945 100644
--- a/src/handlebars.yy
+++ b/src/handlebars.yy
@@ -79,7 +79,7 @@ inverseChain
   ;

 closeBlock
-  : OPEN_ENDBLOCK helperName CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
+  : OPEN_ENDBLOCK partialName CLOSE -> {path: $2, strip: yy.stripFlags($1, $3)}
   ;

 mustache

動的な部分式がそれほど些細なことではないため、これは実際にはあまり良い解決策ではないと思います。したがって、 openPartialBlockcloseBlock部分式を一致させるのは非常に面倒です。

部分式を含むopenPartialBlockにラベルを付ける方法を見つけたかったのですが、既存の部分構文と明確に区​​別できる方法を見つけることができませんでした。 他の構文上の提案を聞きたいのですが、構文についてコンセンサスが得られれば、PRを提出できれば幸いです。

feature

最も参考になるコメント

また、この 'lilハックで_今日_ブロックレベルのヘルパーとして動的パーシャルを使用できることを正しく指摘しているSOスレッドに出くわしまし

{{#>( lookup . 'intendedTemplate' )}}
  No template matched for "{{intendedTemplate}}"
 {{/undefined}}

控えめに言っても、終了タグとして{{/undefined}}を使用すると、ハッキーな感じがします。 それでも、 @ nknappが提案する構文の優雅さは欠けていますが、実際には期待どおりに機能します。

全てのコメント11件

構文の観点からは、インラインパーシャルをサポートするために導入されたデコレータ構文がここに適している可能性があります。 このようなもの:

{{#*dynamic (partialName)}}
Fallback content
{{/dynamic}}

これをデコレータとして実装できるかどうかを知ることは興味深いでしょう(#1372が修正されると仮定して)。

編集:いいえ、私のコメントはばかげていました。 デコレータではありません。 どちらかといえば、それはヘルパーになりますが、ヘルパーは簡単にパーシャルを呼び出すことはできません。
以前にHandlebars.registeryDecorator('dynamic',...)を実行した場合、構文{{#*dynamic ...}}はすでに使用されています。

編集:これについて考えた後、以下はおそらく非常に悪い解決策です。 副作用についてはよくわかりませんが、パブリックとは呼ばない多くのAPIを使用しています...

このようなものを使用して、「すべての」欠落しているパーシャルのデフォルトの文字列を実装できます。

const Handlebars = require('handlebars')

Handlebars.registerDecorator('onMissingPartial', function(program, props, container, decoratorContext) {
  const invokePartialWrapper = container.invokePartial
  container.invokePartial = function (partial, context, options) {
    if (props.partials[options.name]) {
      return invokePartialWrapper.apply(this, Array.prototype.slice.call(arguments))
    }
    return decoratorContext.fn(Object.assign({partialName: options.name, context}))
  }
});

var template = Handlebars.compile(`
{{#*inline "inlinePartial"}}
inlinePartial
{{/inline}}

{{#*onMissingPartial}}
Partial "{{partialName}}" could not be found
{{/onMissingPartial}}

{{>inlinePartial}}
{{>unknownPartial}}
`)

console.log("output", template({name: 'myPartial2'}))

それが示している

Partial "unknownPartial" could not be found

{{> (name) }}では動作しませんが、これは#1372と同様のバグである可能性があります。

最初に仕様(#1277)が必要であり、現時点では誰も仕様を作成していないようであるため、新しい言語機能なしで方法を見つけようとしています。

私たちはこれを実装します、私は最初に提案された{{/}}クロージングブロックに行くと思います。

以前にHandlebars.registeryDecorator( 'dynamic'、...)を実行した場合、構文{{#* dynamic ...}}はすでに使用されています。

dynamicデコレータをすでに登録しているコードがあると言っているのですか、それともコードで登録

あなたそれを登録すること{{#*...}}は「デコレータを適用する」ことを意味するため、 {{#* dyamic}}は「デコレータ」も意味します。 {{#*inline}}、コンテキストにパーシャルを追加するデコレータです。 しかし、動的ブロック部分を呼び出すことは、デコレータのようなものではありません。

{{helper}}はブロックに対して{{#block-helper}}...{{/block-helper}}になり、 {{>partial}}{{#>block-partial}}...{{/block-partial}}になり、 {{> (dynamic-partial}}構文はすでに存在するので、 {{#> (dynamic-block) }}だと思います{{/}}は論理的で、何も壊れません。 (私が見る限り)。

動的パーシャルのフェイルオーバーコンテンツブロックは、ハンドルバーへの_素晴らしい_追加になります。 これについて何か動きはありましたか? それを推進するために私にできることはありますか? (また:彼らの素晴らしい仕事のためにすべての/すべてのHBS協力者に多くの♥!)

また、この 'lilハックで_今日_ブロックレベルのヘルパーとして動的パーシャルを使用できることを正しく指摘しているSOスレッドに出くわしまし

{{#>( lookup . 'intendedTemplate' )}}
  No template matched for "{{intendedTemplate}}"
 {{/undefined}}

控えめに言っても、終了タグとして{{/undefined}}を使用すると、ハッキーな感じがします。 それでも、 @ nknappが提案する構文の優雅さは欠けていますが、実際には期待どおりに機能します。

すべての建設的なフィードバックの後、PRなしでこれを支持して申し訳ありません。 空のクロージングブロックが他の部分ブロックで許可されないようにしようとして立ち往生しましたが、今日すべてに戻って、#1422で意図したとおりに機能していると思います。

PR、@ jstewmonをありがとう。 これは間違いなく言語の機能強化であり、バグを修正するように頼まれたため、最終的な決定は@wycatsが行う必要があると思います。 彼の意図した言語の方向性についてはよくわかりません。
この主要な問題が解決された場合、私はいくつかのコメントがあります:

1) {{#> (partial)}}failover content{{/}}ようなテンプレートを使用して、「部分的」が実際に既存の部分的に解決される1つのテストを見たいと思います。
2)このコミットでパフォーマンスはどのように変化しますか?
3)4.1でこの機能を確認したい場合( @wycatsが承認した場合)、PRを4.xブランチにポイントする必要があります。 後でマスターにマージします。

#1422で実装されましたが、まだマージされていません

このページは役に立ちましたか?
0 / 5 - 0 評価