9

所以我遇到了一些问题,围绕着使用 Mustache.php 灵活处理一些复杂的 html 案例的最佳惯用方法

第一个是预先选择的选择下拉菜单,例如

<select>
  <option value=''></option>
  <option value='bob'>Bob Williams</option>
  <option value='james' selected>James Smith</option>
</select>

我有办法解决这个问题,但我的方式似乎很不灵活:

  • 在php中取一个数组,
  • 将其重新格式化为具有 3 个元素的多维数组;值,显示,选择(布尔)
  • 将其传递给模板,其中选项、值和选定项在循环中输出

是否有一种很棒的方法可以使用部分或匿名函数或方法或我缺少的 mustache.php 的其他一些功能来制作预选的选择下拉菜单?

编辑:将此问题简化为单独的部分,以尽量提高清晰度。

4

2 回答 2

20

在 Mustache 中执行此操作的惯用方法是创建一个视图(或 ViewModel),而不是传入数据散列:

<?php

class Dropdown
{
  public  $name;
  public  $value;
  private $options;

  public function __construct($name, array $options, $value)
  {
    $this->name    = $name;
    $this->options = $options;
    $this->value   = $value;
  }

  public function options()
  {
    $value = $this->value;

    return array_map(function($k, $v) use ($value) {
      return array(
        'value'    => $k,
        'display'  => $v,
        'selected' => ($value === $k),
      )
    }, array_keys($this->options), $this->options);
  }
}

然后你可以将它与dropdown部分...

<select name="{{ name }}">
  {{# options }}
    <option value="{{ value }}"{{# selected }} selected{{/ selected }}>
      {{ display }}
    </option>
  {{/ options }}
</select>

您可以像这样在模板中使用它:

{{# state }}
  <label for="{{ name }}">State</label>
  {{> dropdown }}
{{/ state }}

{{# country }}
  <label for="{{ name }}">Country</label>
  {{> dropdown }}
{{/ country }}

并渲染它:

<?php

$data = array(
  'state'   => new Dropdown('state',   $someListOfStates,    'CA'),
  'country' => new Dropdown('country', $someListOfCountries, 'USA'),
);

$template->render($data);

...但你可以做得更好:)

有了这个:

<?php

class StateDropdown extends Dropdown
{
  static $states = array(...);

  public function __construct($value, $name = 'state')
  {
    parent::__construct($name, self::$states, $value);
  }
}

还有这个:

<?php

class CountryDropdown extends Dropdown
{
  static $countries = array(...);

  public function __construct($value, $name = 'country')
  {
    parent::__construct($name, self::$countries, $value);
  }
}

其中之一:

<?php

class Address
{
  public $street;
  public $city;
  public $state;
  public $zip;
  public $country;

  public function __construct($street, $city, $state, $zip, $country, $name = 'address')
  {
    $this->street  = $street;
    $this->city    = $city;
    $this->state   = new StateDropdown($state, sprintf('%s[state]', $name));
    $this->zip     = $zip;
    $this->country = new CountryDropdown($country, sprintf('%s[country]', $name));
  }
}

加入一个新的address部分:

<label for="{{ name }}[street]">Street</label>
<input type="text" name="{{ name }}[street]" value="{{ street }}">

<label for="{{ name }}[city]">City</label>
<input type="text" name="{{ name }}[city]" value="{{ city }}">

{{# state }}
  <label for="{{ name }}">State</label>
  {{> dropdown }}
{{/ state }}

<label for="{{ name }}[zip]">Postal code</label>
<input type="text" name="{{ name }}[zip]" value="{{ zip }}">

{{# country }}
  <label for="{{ name }}">Country</label>
  {{> dropdown }}
{{/ country }}

更新您的主模板:

<h2>Shipping Address</h2>
{{# shippingAddress }}
  {{> address }}
{{/ shippingAddress }}

<h2>Billing Address</h2>
{{# billingAddress }}
  {{> address }}
{{/ billingAddress }}

去!

<?php

$data = array(
  'shippingAddress' => new Address($shipStreet, $shipCity, $shipState, $shipZip, $shipCountry, 'shipping'),
  'billingAddress'  => new Address($billStreet, $billCity, $billState, $billZip, $billCountry, 'billing'),
};

$template->render($data);

现在,您有了模块化、可重用、易于测试、可扩展的代码和部分代码。

请注意,我们创建的类是“Views”或“ViewModels”。它们不是您的域模型对象...它们不关心持久性或验证,它们只关心为您的模板准备值。如果你也在使用模型,那就更容易了,因为像我们的地址类这样的东西可以包装你的地址模型,并直接从模型中获取它需要的值,而不是要求你将一堆东西传递给构造函数.

胡子之禅

如果您采用这种方法得出其合乎逻辑的结论,您最终会在您的应用程序中为每个操作/模板对拥有一个顶级 View 或 ViewModel 类 — View 可以在内部委托给子 Views 和 partials,就像我们对 Dropdowns 所做的那样我们的地址视图,但您将有一个一流的视图或视图模型负责呈现每个操作。

这意味着(在 MVC/MVVM 世界中),您的 Controller 操作将执行所需的任何“操作”,然后创建负责填充模板的 View 或 ViewModel 类,将几个域模型对象交给它,然后调用 render on模板。控制器不会准备任何数据,因为这是视图层的责任。它会简单地将几个模型对象交给它。

现在你所有的“渲染”逻辑都整齐地封装在视图层中,你所有的标记都整齐地封装在你的模板文件中,你的模型摆脱了丑陋的格式化业务,你的控制器就像它应该的那样漂亮和轻巧:)

于 2012-10-05T19:47:26.400 回答
1

bobthecow 是一个很好的起点,但代码中有一些错误。

我必须更改下拉类以支持 kohana 中的多选下拉菜单,并且 array_map 要求您将相同数量的变量传递给函数。

下拉类:

class Tiaa_Dropdown
{
  public  $name;
  public  $value;
  private $options;

  public function __construct($name, array $options, $value)
  {
    $this->nameAttr    = $name;
    $this->options = $options;
    $this->value   = $value;
  }

  public function things()
  {
    $value = $this->value;
    $array_keys = array_keys($this->options);
    $array_values = array_values($this->options);
    return array_map(function($k, $v) use ($value) {

  if(is_array($value)) {
    $selected = (in_array($k, $value) ? 1 : 0);
  } else {
    $selected = ($value == $k);
  }

     return array(
        'value'    => $k,
        'display'  => $v,
        'selected' =>  $selected
      );
    }, $array_keys, $array_values);
  }
} // End

希望这可以帮助某人。

于 2014-02-28T16:48:53.013 回答