Mustache中的复杂模板组合

时间:2013-02-11 20:51:14

标签: templates template-engine mustache

考虑一个显示用户冰箱内容的Web应用程序。除此之外,还需要显示冰箱中当前的水果列表。每种类型的水果需要非常不同的展示。最重要的是,会有大量不同的水果类型。

我想使用Mustache模板库来实现它,但我不太清楚最好的方法是什么。

首先,以下是每种水果的模板:

{{! apple.mustache }}
A {{appleType}} apple from {{groceryStore}}

{{! orange.mustache }}
An orange from {{countryOfOrigin}}

{{! banana.mustache }}
A {{ripeness}} banana

...many more templates here...

方法1

让“视图模型”或“视图助手”对象通过创建“isBanana”/“isOrange”/等来准备模板数据。将传递给模板的键。在这种情况下,冰箱模板看起来像这样:

You have the following food in your fridge:

{{#fruits}}
        {{#isApple}}
                {{> apple}}
        {{/isApple}}
        {{#isOrange}}
                {{> orange}}
        {{/isOrange}}
        {{#isBanana}}
                {{> banana}}
        {{/isBanana}}
        ...more conditionals....
{{/fruits}}

我看到这种方法推荐了互联网上的几个地方。但是,我无法看到它的扩展方式:每次添加新的水果类型时都必须修改冰箱模板。它似乎也反对胡子的“无逻辑”哲学。

方法2

使视图模型负责确定每种水果类型的正确模板,渲染它,并将HTML作为模板的数据返回。冰箱模板现在看起来像:

You have the following food in your fridge:

{{{fruits}}}

视图模型:

class InventoryViewModel
{
    // ...

    function getFruits()
    {
        foreach ($this->fridge->getFruits() as $fruit) {
            if ($fruit instanceof Banana) {
                $html .= $this->mustache->render(...);
            } elseif ($fruit instanceof Apple) {
                $html .= $this->mustache->render(...);
            } else {
                // ...
            }
        }

        return $html;
    }
}

似乎比第一个选项更好,但它需要在每个视图模型中注入一个胡子模板渲染对象。如果可能的话,我想避免这种耦合。

方法3

使用某种模板组合功能,这不是官方Mustache规范的一部分。 (https://github.com/janl/mustache.js/pull/242https://github.com/mustache/spec/issues/38等。)

以下哪个选项最好?为什么?我是否忽略了更好的东西?

1 个答案:

答案 0 :(得分:3)

最简单的方法是在ViewModel中使用高阶部分(lambda部分)来为你选择部分部分。

你的基本模板看起来像这样:

{{# items }}
  {{# getPartial }}{{ type }}{{/ getPartial }}
{{/ items }}

当然,假设你的水果有'类型'。然后,您需要为getPartial添加更高阶的部分:

<?php

class InventoryViewModel
{
    // ...

    function getPartial()
    {
        // you could do string manipulation here,
        // but I prefer the explicitness of a `case`

        return function($tpl, $mustache) {
            switch ($mustache->render($tpl)) {
                case 'Banana':
                    return '{{> banana }}';
                case 'Apple':
                    return '{{> apple }}';
                case 'Orange':
                    return '{{> orange }}';
            }
        };
    }
}

在这种情况下,通过将逻辑移动到其附带的ViewModel,您的模板将保持“无逻辑”,可以对其进行单元测试,模拟等等。